diff options
author | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-08 14:30:41 +0200 |
---|---|---|
committer | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-12 13:49:54 +0200 |
commit | ab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch) | |
tree | 498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/ui/views | |
parent | 4ce69f7403811819800e7c5ae1318b2647e778d1 (diff) |
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca
Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/ui/views')
555 files changed, 23756 insertions, 26929 deletions
diff --git a/chromium/ui/views/DEPS b/chromium/ui/views/DEPS index 7619dc0cb17..b29fbd88a07 100644 --- a/chromium/ui/views/DEPS +++ b/chromium/ui/views/DEPS @@ -4,13 +4,20 @@ include_rules = [ "+skia/ext", "+third_party/iaccessible2", "+third_party/skia", + "+ui/accessibility", "+ui/aura", "+ui/base", "+ui/compositor", + "+ui/display", "+ui/events", "+ui/gfx", - "+ui/ozone", + "+ui/gl/gl_surface.h", # To initialize GL for tests. "+ui/native_theme", + "+ui/ozone", + "+ui/wm/core", + "+ui/wm/public", + + "-testing/gmock", ] specific_include_rules = { diff --git a/chromium/ui/views/accessibility/ax_aura_obj_cache.cc b/chromium/ui/views/accessibility/ax_aura_obj_cache.cc new file mode 100644 index 00000000000..ae2df2068ab --- /dev/null +++ b/chromium/ui/views/accessibility/ax_aura_obj_cache.cc @@ -0,0 +1,130 @@ +// Copyright 2014 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/accessibility/ax_aura_obj_cache.h" + +#include "base/memory/singleton.h" +#include "base/stl_util.h" +#include "ui/aura/window.h" +#include "ui/views/accessibility/ax_aura_obj_wrapper.h" +#include "ui/views/accessibility/ax_view_obj_wrapper.h" +#include "ui/views/accessibility/ax_widget_obj_wrapper.h" +#include "ui/views/accessibility/ax_window_obj_wrapper.h" +#include "ui/views/view.h" +#include "ui/views/widget/widget.h" + +namespace views { + +// static +AXAuraObjCache* AXAuraObjCache::GetInstance() { + return Singleton<AXAuraObjCache>::get(); +} + +AXAuraObjWrapper* AXAuraObjCache::GetOrCreate(View* view) { + return CreateInternal<AXViewObjWrapper>(view, view_to_id_map_); +} + +AXAuraObjWrapper* AXAuraObjCache::GetOrCreate(Widget* widget) { + return CreateInternal<AXWidgetObjWrapper>(widget, widget_to_id_map_); +} + +AXAuraObjWrapper* AXAuraObjCache::GetOrCreate(aura::Window* window) { + return CreateInternal<AXWindowObjWrapper>(window, window_to_id_map_); +} + +int32 AXAuraObjCache::GetID(View* view) { + return GetIDInternal(view, view_to_id_map_); +} + +int32 AXAuraObjCache::GetID(Widget* widget) { + return GetIDInternal(widget, widget_to_id_map_); +} + +int32 AXAuraObjCache::GetID(aura::Window* window) { + return GetIDInternal(window, window_to_id_map_); +} + +void AXAuraObjCache::Remove(View* view) { + RemoveInternal(view, view_to_id_map_); +} + +void AXAuraObjCache::Remove(Widget* widget) { + RemoveInternal(widget, widget_to_id_map_); +} + +void AXAuraObjCache::Remove(aura::Window* window) { + RemoveInternal(window, window_to_id_map_); +} + +AXAuraObjWrapper* AXAuraObjCache::Get(int32 id) { + std::map<int32, AXAuraObjWrapper*>::iterator it = cache_.find(id); + + if (it == cache_.end()) + return NULL; + + return it->second; +} + +void AXAuraObjCache::Remove(int32 id) { + AXAuraObjWrapper* obj = Get(id); + + if (id == -1 || !obj) + return; + + cache_.erase(id); + delete obj; +} + +AXAuraObjCache::AXAuraObjCache() : current_id_(1) { +} + +AXAuraObjCache::~AXAuraObjCache() { + STLDeleteContainerPairSecondPointers(cache_.begin(), cache_.end()); + cache_.clear(); +} + +template <typename AuraViewWrapper, typename AuraView> +AXAuraObjWrapper* AXAuraObjCache::CreateInternal( + AuraView* aura_view, std::map<AuraView*, int32>& aura_view_to_id_map) { + if (!aura_view) + return NULL; + + typename std::map<AuraView*, int32>::iterator it = + aura_view_to_id_map.find(aura_view); + + if (it != aura_view_to_id_map.end()) + return Get(it->second); + + AXAuraObjWrapper* wrapper = new AuraViewWrapper(aura_view); + aura_view_to_id_map[aura_view] = current_id_; + cache_[current_id_] = wrapper; + current_id_++; + return wrapper; +} + +template<typename AuraView> int32 AXAuraObjCache::GetIDInternal( + AuraView* aura_view, std::map<AuraView*, int32>& aura_view_to_id_map) { + if (!aura_view) + return -1; + + typename std::map<AuraView*, int32>::iterator it = + aura_view_to_id_map.find(aura_view); + + if (it != aura_view_to_id_map.end()) + return it->second; + + return -1; +} + +template<typename AuraView> +void AXAuraObjCache::RemoveInternal( + AuraView* aura_view, std::map<AuraView*, int32>& aura_view_to_id_map) { + int32 id = GetID(aura_view); + if (id == -1) + return; + aura_view_to_id_map.erase(aura_view); + Remove(id); +} + +} // namespace views diff --git a/chromium/ui/views/accessibility/ax_aura_obj_cache.h b/chromium/ui/views/accessibility/ax_aura_obj_cache.h new file mode 100644 index 00000000000..6e5d9e829a9 --- /dev/null +++ b/chromium/ui/views/accessibility/ax_aura_obj_cache.h @@ -0,0 +1,83 @@ +// Copyright 2014 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_ACCESSIBILITY_AX_AURA_OBJ_CACHE_H_ +#define UI_VIEWS_ACCESSIBILITY_AX_AURA_OBJ_CACHE_H_ + +#include <map> + +#include "base/basictypes.h" +#include "ui/views/views_export.h" + +template <typename T> struct DefaultSingletonTraits; + +namespace aura { +class Window; +} // namespace aura + +namespace views { +class AXAuraObjWrapper; +class View; +class Widget; + +// A cache responsible for assigning id's to a set of interesting Aura views. +class VIEWS_EXPORT AXAuraObjCache { + public: + // Get the single instance of this class. + static AXAuraObjCache* GetInstance(); + + // Get or create an entry in the cache based on an Aura view. + AXAuraObjWrapper* GetOrCreate(View* view); + AXAuraObjWrapper* GetOrCreate(Widget* widget); + AXAuraObjWrapper* GetOrCreate(aura::Window* window); + + // Gets an id given an Aura view. + int32 GetID(View* view); + int32 GetID(Widget* widget); + int32 GetID(aura::Window* window); + + // Gets the next unique id for this cache. Useful for non-Aura view backed + // views. + int32 GetNextID() { return current_id_++; } + + // Removes an entry from this cache based on an Aura view. + void Remove(View* view); + void Remove(Widget* widget); + void Remove(aura::Window* window); + + // Lookup a cached entry based on an id. + AXAuraObjWrapper* Get(int32 id); + + // Remove a cached entry based on an id. + void Remove(int32 id); + + private: + friend struct DefaultSingletonTraits<AXAuraObjCache>; + + AXAuraObjCache(); + virtual ~AXAuraObjCache(); + + template <typename AuraViewWrapper, typename AuraView> + AXAuraObjWrapper* CreateInternal( + AuraView* aura_view, std::map<AuraView*, int32>& aura_view_to_id_map); + + template<typename AuraView> int32 GetIDInternal( + AuraView* aura_view, std::map<AuraView*, int32>& aura_view_to_id_map); + + template<typename AuraView> void RemoveInternal( + AuraView* aura_view, std::map<AuraView*, int32>& aura_view_to_id_map); + + std::map<views::View*, int32> view_to_id_map_; + std::map<views::Widget*, int32> widget_to_id_map_; + std::map<aura::Window*, int32> window_to_id_map_; + + std::map<int32, AXAuraObjWrapper*> cache_; + int32 current_id_; + + DISALLOW_COPY_AND_ASSIGN(AXAuraObjCache); +}; + +} // namespace views + +#endif // UI_VIEWS_ACCESSIBILITY_AX_AURA_OBJ_CACHE_H_ diff --git a/chromium/ui/views/accessibility/ax_aura_obj_wrapper.h b/chromium/ui/views/accessibility/ax_aura_obj_wrapper.h new file mode 100644 index 00000000000..00f9c593c0c --- /dev/null +++ b/chromium/ui/views/accessibility/ax_aura_obj_wrapper.h @@ -0,0 +1,43 @@ +// Copyright 2014 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_ACCESSIBILITY_AX_AURA_OBJ_WRAPPER_H_ +#define UI_VIEWS_ACCESSIBILITY_AX_AURA_OBJ_WRAPPER_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "ui/views/views_export.h" + +namespace ui { +struct AXNodeData; +} // namespace ui + +namespace views { + +// An interface abstraction for Aura views that exposes the view-tree formed +// by the implementing view types. +class VIEWS_EXPORT AXAuraObjWrapper { + public: + virtual ~AXAuraObjWrapper() {} + + // Traversal and serialization. + virtual AXAuraObjWrapper* GetParent() = 0; + virtual void GetChildren( + std::vector<AXAuraObjWrapper*>* out_children) = 0; + virtual void Serialize(ui::AXNodeData* out_node_data) = 0; + virtual int32 GetID() = 0; + + // Actions. + virtual void DoDefault() {} + virtual void Focus() {} + virtual void MakeVisible() {} + virtual void SetSelection(int32 start, int32 end) {} +}; + +} // namespace views + +#endif // UI_VIEWS_ACCESSIBILITY_AX_AURA_OBJ_WRAPPER_H_ diff --git a/chromium/ui/views/accessibility/ax_view_obj_wrapper.cc b/chromium/ui/views/accessibility/ax_view_obj_wrapper.cc new file mode 100644 index 00000000000..b5c60775c75 --- /dev/null +++ b/chromium/ui/views/accessibility/ax_view_obj_wrapper.cc @@ -0,0 +1,91 @@ +// Copyright 2014 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/accessibility/ax_view_obj_wrapper.h" + +#include "base/strings/utf_string_conversions.h" +#include "ui/accessibility/ax_node_data.h" +#include "ui/accessibility/ax_view_state.h" +#include "ui/views/accessibility/ax_aura_obj_cache.h" +#include "ui/views/view.h" +#include "ui/views/widget/widget.h" + +namespace views { + +AXViewObjWrapper::AXViewObjWrapper(View* view) : view_(view) { + DCHECK(view->GetWidget()); + if (view->GetWidget()) + AXAuraObjCache::GetInstance()->GetOrCreate(view->GetWidget()); +} + +AXViewObjWrapper::~AXViewObjWrapper() {} + +AXAuraObjWrapper* AXViewObjWrapper::GetParent() { + AXAuraObjCache* cache = AXAuraObjCache::GetInstance(); + if (view_->parent()) + return cache->GetOrCreate(view_->parent()); + + return cache->GetOrCreate(view_->GetWidget()); +} + +void AXViewObjWrapper::GetChildren( + std::vector<AXAuraObjWrapper*>* out_children) { + // TODO(dtseng): Need to handle |Widget| child of |View|. + for (int i = 0; i < view_->child_count(); ++i) { + AXAuraObjWrapper* child = + AXAuraObjCache::GetInstance()->GetOrCreate(view_->child_at(i)); + out_children->push_back(child); + } +} + +void AXViewObjWrapper::Serialize(ui::AXNodeData* out_node_data) { + ui::AXViewState view_data; + view_->GetAccessibleState(&view_data); + out_node_data->id = GetID(); + out_node_data->role = view_data.role; + + out_node_data->state = view_data.state(); + if (view_->HasFocus()) + out_node_data->state |= 1 << ui::AX_STATE_FOCUSED; + if (view_->IsFocusable()) + out_node_data->state |= 1 << ui::AX_STATE_FOCUSABLE; + + out_node_data->location = view_->GetBoundsInScreen(); + + out_node_data->AddStringAttribute( + ui::AX_ATTR_NAME, base::UTF16ToUTF8(view_data.name)); + out_node_data->AddStringAttribute( + ui::AX_ATTR_VALUE, base::UTF16ToUTF8(view_data.value)); + + out_node_data->AddIntAttribute(ui::AX_ATTR_TEXT_SEL_START, + view_data.selection_start); + out_node_data->AddIntAttribute(ui::AX_ATTR_TEXT_SEL_END, + view_data.selection_end); +} + +int32 AXViewObjWrapper::GetID() { + return AXAuraObjCache::GetInstance()->GetID(view_); +} + +void AXViewObjWrapper::DoDefault() { + gfx::Rect rect = view_->GetBoundsInScreen(); + gfx::Point center = rect.CenterPoint(); + view_->OnMousePressed(ui::MouseEvent(ui::ET_MOUSE_PRESSED, center, center, + ui::EF_LEFT_MOUSE_BUTTON, + ui::EF_LEFT_MOUSE_BUTTON)); +} + +void AXViewObjWrapper::Focus() { + view_->RequestFocus(); +} + +void AXViewObjWrapper::MakeVisible() { + // TODO(dtseng): Implement. +} + +void AXViewObjWrapper::SetSelection(int32 start, int32 end) { + // TODO(dtseng): Implement. +} + +} // namespace views diff --git a/chromium/ui/views/accessibility/ax_view_obj_wrapper.h b/chromium/ui/views/accessibility/ax_view_obj_wrapper.h new file mode 100644 index 00000000000..6838d07243c --- /dev/null +++ b/chromium/ui/views/accessibility/ax_view_obj_wrapper.h @@ -0,0 +1,38 @@ +// Copyright 2014 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_ACCESSIBILITY_AX_VIEW_OBJ_WRAPPER_H_ +#define UI_VIEWS_ACCESSIBILITY_AX_VIEW_OBJ_WRAPPER_H_ + +#include "ui/views/accessibility/ax_aura_obj_wrapper.h" + +namespace views { +class View; + +// Describes a |View| for use with other AX classes. +class AXViewObjWrapper : public AXAuraObjWrapper { + public: + explicit AXViewObjWrapper(View* view); + virtual ~AXViewObjWrapper(); + + // AXAuraObjWrapper overrides. + virtual AXAuraObjWrapper* GetParent() OVERRIDE; + virtual void GetChildren( + std::vector<AXAuraObjWrapper*>* out_children) OVERRIDE; + virtual void Serialize(ui::AXNodeData* out_node_data) OVERRIDE; + virtual int32 GetID() OVERRIDE; + virtual void DoDefault() OVERRIDE; + virtual void Focus() OVERRIDE; + virtual void MakeVisible() OVERRIDE; + virtual void SetSelection(int32 start, int32 end) OVERRIDE; + + private: + View* view_; + + DISALLOW_COPY_AND_ASSIGN(AXViewObjWrapper); +}; + +} // namespace views + +#endif // UI_VIEWS_ACCESSIBILITY_AX_VIEW_OBJ_WRAPPER_H_ diff --git a/chromium/ui/views/accessibility/ax_widget_obj_wrapper.cc b/chromium/ui/views/accessibility/ax_widget_obj_wrapper.cc new file mode 100644 index 00000000000..36a3143e365 --- /dev/null +++ b/chromium/ui/views/accessibility/ax_widget_obj_wrapper.cc @@ -0,0 +1,55 @@ +// Copyright 2014 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/accessibility/ax_widget_obj_wrapper.h" + +#include "ui/accessibility/ax_node_data.h" +#include "ui/views/accessibility/ax_aura_obj_cache.h" +#include "ui/views/accessibility/ax_aura_obj_wrapper.h" +#include "ui/views/widget/widget.h" + +namespace views { + +AXWidgetObjWrapper::AXWidgetObjWrapper(Widget* widget) : widget_(widget) { + widget->AddObserver(this); + widget->AddRemovalsObserver(this); +} + +AXWidgetObjWrapper::~AXWidgetObjWrapper() { + widget_->RemoveObserver(this); + widget_->RemoveRemovalsObserver(this); + widget_ = NULL; +} + +AXAuraObjWrapper* AXWidgetObjWrapper::GetParent() { + return AXAuraObjCache::GetInstance()->GetOrCreate(widget_->GetNativeView()); +} + +void AXWidgetObjWrapper::GetChildren( + std::vector<AXAuraObjWrapper*>* out_children) { + out_children->push_back( + AXAuraObjCache::GetInstance()->GetOrCreate(widget_->GetRootView())); +} + +void AXWidgetObjWrapper::Serialize(ui::AXNodeData* out_node_data) { + out_node_data->id = GetID(); + out_node_data->role = ui::AX_ROLE_CLIENT; + out_node_data->location = widget_->GetWindowBoundsInScreen(); + // TODO(dtseng): Set better states. + out_node_data->state = 0; +} + +int32 AXWidgetObjWrapper::GetID() { + return AXAuraObjCache::GetInstance()->GetID(widget_); +} + +void AXWidgetObjWrapper::OnWidgetDestroying(Widget* widget) { + AXAuraObjCache::GetInstance()->Remove(widget); +} + +void AXWidgetObjWrapper::OnWillRemoveView(Widget* widget, View* view) { + AXAuraObjCache::GetInstance()->Remove(view); +} + +} // namespace views diff --git a/chromium/ui/views/accessibility/ax_widget_obj_wrapper.h b/chromium/ui/views/accessibility/ax_widget_obj_wrapper.h new file mode 100644 index 00000000000..61ff1e90f28 --- /dev/null +++ b/chromium/ui/views/accessibility/ax_widget_obj_wrapper.h @@ -0,0 +1,44 @@ +// Copyright 2014 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_ACCESSIBILITY_AX_WIDGET_OBJ_WRAPPER_H_ +#define UI_VIEWS_ACCESSIBILITY_AX_WIDGET_OBJ_WRAPPER_H_ + +#include "ui/views/accessibility/ax_aura_obj_wrapper.h" +#include "ui/views/widget/widget_observer.h" +#include "ui/views/widget/widget_removals_observer.h" + +namespace views { +class Widget; + +// Describes a |Widget| for use with other AX classes. +class AXWidgetObjWrapper : public AXAuraObjWrapper, + public WidgetObserver, + public WidgetRemovalsObserver { + public: + explicit AXWidgetObjWrapper(Widget* widget); + virtual ~AXWidgetObjWrapper(); + + // AXAuraObjWrapper overrides. + virtual AXAuraObjWrapper* GetParent() OVERRIDE; + virtual void GetChildren( + std::vector<AXAuraObjWrapper*>* out_children) OVERRIDE; + virtual void Serialize(ui::AXNodeData* out_node_data) OVERRIDE; + virtual int32 GetID() OVERRIDE; + + // WidgetObserver overrides. + virtual void OnWidgetDestroying(Widget* widget) OVERRIDE; + + // WidgetRemovalsObserver overrides. + virtual void OnWillRemoveView(Widget* widget, View* view) OVERRIDE; + + private: + Widget* widget_; + + DISALLOW_COPY_AND_ASSIGN(AXWidgetObjWrapper); +}; + +} // namespace views + +#endif // UI_VIEWS_ACCESSIBILITY_AX_WIDGET_OBJ_WRAPPER_H_ diff --git a/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc b/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc new file mode 100644 index 00000000000..b358de58c91 --- /dev/null +++ b/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc @@ -0,0 +1,64 @@ +// Copyright 2014 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/accessibility/ax_window_obj_wrapper.h" + +#include "base/strings/utf_string_conversions.h" +#include "ui/accessibility/ax_node_data.h" +#include "ui/aura/window.h" +#include "ui/views/accessibility/ax_aura_obj_cache.h" +#include "ui/views/widget/widget.h" + +namespace views { + +AXWindowObjWrapper::AXWindowObjWrapper( + aura::Window* window) : window_(window) { + window->AddObserver(this); +} + +AXWindowObjWrapper::~AXWindowObjWrapper() { + window_->RemoveObserver(this); + window_ = NULL; +} + +AXAuraObjWrapper* AXWindowObjWrapper::GetParent() { + if (!window_->parent()) + return NULL; + + return AXAuraObjCache::GetInstance()->GetOrCreate(window_->parent()); +} + +void AXWindowObjWrapper::GetChildren( + std::vector<AXAuraObjWrapper*>* out_children) { + aura::Window::Windows children = window_->children(); + for (size_t i = 0; i < children.size(); ++i) { + out_children->push_back( + AXAuraObjCache::GetInstance()->GetOrCreate(children[i])); + } + + // Also consider any associated widgets as children. + Widget* widget = Widget::GetWidgetForNativeView(window_); + if (widget) + out_children->push_back(AXAuraObjCache::GetInstance()->GetOrCreate(widget)); +} + +void AXWindowObjWrapper::Serialize(ui::AXNodeData* out_node_data) { + out_node_data->id = GetID(); + out_node_data->role = ui::AX_ROLE_WINDOW; + // TODO(dtseng): Set better states. + out_node_data->state = 0; + out_node_data->location = window_->bounds(); + out_node_data->AddStringAttribute( + ui::AX_ATTR_NAME, base::UTF16ToUTF8(window_->title())); +} + +int32 AXWindowObjWrapper::GetID() { + return AXAuraObjCache::GetInstance()->GetID(window_); +} + +void AXWindowObjWrapper::OnWindowDestroying(aura::Window* window) { + AXAuraObjCache::GetInstance()->Remove(window); +} + +} // namespace views diff --git a/chromium/ui/views/accessibility/ax_window_obj_wrapper.h b/chromium/ui/views/accessibility/ax_window_obj_wrapper.h new file mode 100644 index 00000000000..b3cd235cfb7 --- /dev/null +++ b/chromium/ui/views/accessibility/ax_window_obj_wrapper.h @@ -0,0 +1,42 @@ +// Copyright 2014 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_ACCESSIBILITY_AX_WINDOW_OBJ_WRAPPER_H_ +#define UI_VIEWS_ACCESSIBILITY_AX_WINDOW_OBJ_WRAPPER_H_ + +#include "ui/aura/window_observer.h" +#include "ui/views/accessibility/ax_aura_obj_wrapper.h" + +namespace aura { +class Window; +} // namespace aura + +namespace views { + +// Describes a |Window| for use with other AX classes. +class AXWindowObjWrapper : public AXAuraObjWrapper, + public aura::WindowObserver { + public: + explicit AXWindowObjWrapper(aura::Window* window); + virtual ~AXWindowObjWrapper(); + + // AXAuraObjWrapper overrides. + virtual AXAuraObjWrapper* GetParent() OVERRIDE; + virtual void GetChildren( + std::vector<AXAuraObjWrapper*>* out_children) OVERRIDE; + virtual void Serialize(ui::AXNodeData* out_node_data) OVERRIDE; + virtual int32 GetID() OVERRIDE; + + // WindowObserver overrides. + virtual void OnWindowDestroying(aura::Window* window) OVERRIDE; + + private: + aura::Window* window_; + + DISALLOW_COPY_AND_ASSIGN(AXWindowObjWrapper); +}; + +} // namespace views + +#endif // UI_VIEWS_ACCESSIBILITY_AX_WINDOW_OBJ_WRAPPER_H_ diff --git a/chromium/ui/views/accessibility/native_view_accessibility.h b/chromium/ui/views/accessibility/native_view_accessibility.h index 2fc4c4423b6..2b9f5927710 100644 --- a/chromium/ui/views/accessibility/native_view_accessibility.h +++ b/chromium/ui/views/accessibility/native_view_accessibility.h @@ -5,7 +5,7 @@ #ifndef UI_VIEWS_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_H_ #define UI_VIEWS_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_H_ -#include "ui/base/accessibility/accessibility_types.h" +#include "ui/accessibility/ax_enums.h" #include "ui/gfx/native_widget_types.h" #include "ui/views/views_export.h" @@ -18,7 +18,7 @@ class VIEWS_EXPORT NativeViewAccessibility { static NativeViewAccessibility* Create(View* view); virtual void NotifyAccessibilityEvent( - ui::AccessibilityTypes::Event event_type) {} + ui::AXEvent event_type) {} virtual gfx::NativeViewAccessible GetNativeObject(); diff --git a/chromium/ui/views/accessibility/native_view_accessibility_win.cc b/chromium/ui/views/accessibility/native_view_accessibility_win.cc index 3e20cbbc4a9..670de4dd8ec 100644 --- a/chromium/ui/views/accessibility/native_view_accessibility_win.cc +++ b/chromium/ui/views/accessibility/native_view_accessibility_win.cc @@ -4,8 +4,8 @@ #include "ui/views/accessibility/native_view_accessibility_win.h" -#include <UIAutomationClient.h> #include <oleacc.h> +#include <UIAutomationClient.h> #include <set> #include <vector> @@ -15,8 +15,9 @@ #include "base/win/scoped_comptr.h" #include "base/win/windows_version.h" #include "third_party/iaccessible2/ia2_api_all.h" -#include "ui/base/accessibility/accessible_text_utils.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_enums.h" +#include "ui/accessibility/ax_text_utils.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/base/win/accessibility_ids_win.h" #include "ui/base/win/accessibility_misc_utils.h" #include "ui/base/win/atl_module.h" @@ -26,8 +27,6 @@ #include "ui/views/widget/widget.h" #include "ui/views/win/hwnd_util.h" -using ui::AccessibilityTypes; - namespace views { namespace { @@ -190,6 +189,7 @@ void AccessibleWebViewRegistry::QueryIAccessible2Interface(View* web_view) { long NativeViewAccessibilityWin::next_unique_id_ = 1; int NativeViewAccessibilityWin::view_storage_ids_[kMaxViewStorageIds] = {0}; int NativeViewAccessibilityWin::next_view_storage_id_index_ = 0; +std::vector<int> NativeViewAccessibilityWin::alert_target_view_storage_ids_; // static NativeViewAccessibility* NativeViewAccessibility::Create(View* view) { @@ -211,10 +211,11 @@ NativeViewAccessibilityWin::NativeViewAccessibilityWin() } NativeViewAccessibilityWin::~NativeViewAccessibilityWin() { + RemoveAlertTarget(); } void NativeViewAccessibilityWin::NotifyAccessibilityEvent( - ui::AccessibilityTypes::Event event_type) { + ui::AXEvent event_type) { if (!view_) return; @@ -237,6 +238,10 @@ void NativeViewAccessibilityWin::NotifyAccessibilityEvent( ::NotifyWinEvent(MSAAEvent(event_type), hwnd, OBJID_CLIENT, child_id); next_view_storage_id_index_ = (next_view_storage_id_index_ + 1) % kMaxViewStorageIds; + + // Keep track of views that are a target of an alert event. + if (event_type == ui::AX_EVENT_ALERT) + AddAlertTarget(); } gfx::NativeViewAccessible NativeViewAccessibilityWin::GetNativeObject() { @@ -557,9 +562,9 @@ STDMETHODIMP NativeViewAccessibilityWin::get_accDefaultAction( if (!view_) return E_FAIL; - ui::AccessibleViewState state; + ui::AXViewState state; view_->GetAccessibleState(&state); - string16 temp_action = state.default_action; + base::string16 temp_action = state.default_action; if (!temp_action.empty()) { *def_action = SysAllocString(temp_action.c_str()); @@ -578,7 +583,7 @@ STDMETHODIMP NativeViewAccessibilityWin::get_accDescription( if (!view_) return E_FAIL; - string16 temp_desc; + base::string16 temp_desc; view_->GetTooltipText(gfx::Point(), &temp_desc); if (!temp_desc.empty()) { @@ -624,9 +629,9 @@ STDMETHODIMP NativeViewAccessibilityWin::get_accKeyboardShortcut( if (!view_) return E_FAIL; - ui::AccessibleViewState state; + ui::AXViewState state; view_->GetAccessibleState(&state); - string16 temp_key = state.keyboard_shortcut; + base::string16 temp_key = state.keyboard_shortcut; if (!temp_key.empty()) { *acc_key = SysAllocString(temp_key.c_str()); @@ -646,9 +651,9 @@ STDMETHODIMP NativeViewAccessibilityWin::get_accName( return E_FAIL; // Retrieve the current view's name. - ui::AccessibleViewState state; + ui::AXViewState state; view_->GetAccessibleState(&state); - string16 temp_name = state.name; + base::string16 temp_name = state.name; if (!temp_name.empty()) { // Return name retrieved. *name = SysAllocString(temp_name.c_str()); @@ -694,7 +699,7 @@ STDMETHODIMP NativeViewAccessibilityWin::get_accRole( if (!view_) return E_FAIL; - ui::AccessibleViewState state; + ui::AXViewState state; view_->GetAccessibleState(&state); role->vt = VT_I4; role->lVal = MSAARole(state.role); @@ -733,9 +738,9 @@ STDMETHODIMP NativeViewAccessibilityWin::get_accValue(VARIANT var_id, return E_FAIL; // Retrieve the current view's value. - ui::AccessibleViewState state; + ui::AXViewState state; view_->GetAccessibleState(&state); - string16 temp_value = state.value; + base::string16 temp_value = state.value; if (!temp_value.empty()) { // Return value retrieved. @@ -758,7 +763,7 @@ STDMETHODIMP NativeViewAccessibilityWin::put_accValue(VARIANT var_id, return E_FAIL; // Return an error if the view can't set the value. - ui::AccessibleViewState state; + ui::AXViewState state; view_->GetAccessibleState(&state); if (state.set_value_callback.is_null()) return E_FAIL; @@ -782,9 +787,9 @@ STDMETHODIMP NativeViewAccessibilityWin::accSelect( STDMETHODIMP NativeViewAccessibilityWin::get_accHelp( VARIANT var_id, BSTR* help) { - if (help) - *help = NULL; - return E_NOTIMPL; + base::string16 temp = base::UTF8ToUTF16(view_->GetClassName()); + *help = SysAllocString(temp.c_str()); + return S_OK; } STDMETHODIMP NativeViewAccessibilityWin::get_accHelpTopic( @@ -815,7 +820,7 @@ STDMETHODIMP NativeViewAccessibilityWin::role(LONG* role) { if (!role) return E_INVALIDARG; - ui::AccessibleViewState state; + ui::AXViewState state; view_->GetAccessibleState(&state); *role = MSAARole(state.role); return S_OK; @@ -831,14 +836,14 @@ STDMETHODIMP NativeViewAccessibilityWin::get_states(AccessibleStates* states) { if (!states) return E_INVALIDARG; - ui::AccessibleViewState state; + ui::AXViewState state; view_->GetAccessibleState(&state); // There are only a couple of states we need to support // in IAccessible2. If any more are added, we may want to // add a helper function like MSAAState. *states = IA2_STATE_OPAQUE; - if (state.state & AccessibilityTypes::STATE_EDITABLE) + if (state.HasStateFlag(ui::AX_STATE_EDITABLE)) *states |= IA2_STATE_EDITABLE; return S_OK; @@ -866,6 +871,59 @@ STDMETHODIMP NativeViewAccessibilityWin::get_windowHandle(HWND* window_handle) { return *window_handle ? S_OK : S_FALSE; } +STDMETHODIMP NativeViewAccessibilityWin::get_relationTargetsOfType( + BSTR type_bstr, + long max_targets, + IUnknown ***targets, + long *n_targets) { + if (!view_) + return E_FAIL; + + if (!targets || !n_targets) + return E_INVALIDARG; + + *n_targets = 0; + *targets = NULL; + + // Only respond to requests for relations of type "alerts" on the + // root view. + base::string16 type(type_bstr); + if (type != L"alerts" || view_->parent()) + return S_FALSE; + + // Collect all of the alert views that are still valid. + std::vector<View*> alert_views; + ViewStorage* view_storage = ViewStorage::GetInstance(); + for (size_t i = 0; i < alert_target_view_storage_ids_.size(); ++i) { + int view_storage_id = alert_target_view_storage_ids_[i]; + View* view = view_storage->RetrieveView(view_storage_id); + if (!view || !view_->Contains(view)) + continue; + alert_views.push_back(view); + } + + long count = alert_views.size(); + if (count == 0) + return S_FALSE; + + // Don't return more targets than max_targets - but note that the caller + // is allowed to specify max_targets=0 to mean no limit. + if (max_targets > 0 && count > max_targets) + count = max_targets; + + // Return the number of targets. + *n_targets = count; + + // Allocate COM memory for the result array and populate it. + *targets = static_cast<IUnknown**>( + CoTaskMemAlloc(count * sizeof(IUnknown*))); + for (long i = 0; i < count; ++i) { + (*targets)[i] = alert_views[i]->GetNativeViewAccessible(); + (*targets)[i]->AddRef(); + } + return S_OK; +} + // // IAccessibleText // @@ -877,7 +935,7 @@ STDMETHODIMP NativeViewAccessibilityWin::get_nCharacters(LONG* n_characters) { if (!n_characters) return E_INVALIDARG; - string16 text = TextForIAccessibleText(); + base::string16 text = TextForIAccessibleText(); *n_characters = static_cast<LONG>(text.size()); return S_OK; } @@ -889,7 +947,7 @@ STDMETHODIMP NativeViewAccessibilityWin::get_caretOffset(LONG* offset) { if (!offset) return E_INVALIDARG; - ui::AccessibleViewState state; + ui::AXViewState state; view_->GetAccessibleState(&state); *offset = static_cast<LONG>(state.selection_end); return S_OK; @@ -902,7 +960,7 @@ STDMETHODIMP NativeViewAccessibilityWin::get_nSelections(LONG* n_selections) { if (!n_selections) return E_INVALIDARG; - ui::AccessibleViewState state; + ui::AXViewState state; view_->GetAccessibleState(&state); if (state.selection_start != state.selection_end) *n_selections = 1; @@ -920,7 +978,7 @@ STDMETHODIMP NativeViewAccessibilityWin::get_selection(LONG selection_index, if (!start_offset || !end_offset || selection_index != 0) return E_INVALIDARG; - ui::AccessibleViewState state; + ui::AXViewState state; view_->GetAccessibleState(&state); *start_offset = static_cast<LONG>(state.selection_start); *end_offset = static_cast<LONG>(state.selection_end); @@ -933,9 +991,9 @@ STDMETHODIMP NativeViewAccessibilityWin::get_text(LONG start_offset, if (!view_) return E_FAIL; - ui::AccessibleViewState state; + ui::AXViewState state; view_->GetAccessibleState(&state); - string16 text_str = TextForIAccessibleText(); + base::string16 text_str = TextForIAccessibleText(); LONG len = static_cast<LONG>(text_str.size()); if (start_offset == IA2_TEXT_OFFSET_LENGTH) { @@ -963,7 +1021,8 @@ STDMETHODIMP NativeViewAccessibilityWin::get_text(LONG start_offset, if (end_offset > len) return E_INVALIDARG; - string16 substr = text_str.substr(start_offset, end_offset - start_offset); + base::string16 substr = + text_str.substr(start_offset, end_offset - start_offset); if (substr.empty()) return S_FALSE; @@ -989,7 +1048,7 @@ STDMETHODIMP NativeViewAccessibilityWin::get_textAtOffset( return S_FALSE; } - const string16& text_str = TextForIAccessibleText(); + const base::string16& text_str = TextForIAccessibleText(); *start_offset = FindBoundary( text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION); @@ -1015,7 +1074,7 @@ STDMETHODIMP NativeViewAccessibilityWin::get_textBeforeOffset( return S_FALSE; } - const string16& text_str = TextForIAccessibleText(); + const base::string16& text_str = TextForIAccessibleText(); *start_offset = FindBoundary( text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION); @@ -1040,7 +1099,7 @@ STDMETHODIMP NativeViewAccessibilityWin::get_textAfterOffset( return S_FALSE; } - const string16& text_str = TextForIAccessibleText(); + const base::string16& text_str = TextForIAccessibleText(); *start_offset = offset; *end_offset = FindBoundary( @@ -1071,11 +1130,12 @@ STDMETHODIMP NativeViewAccessibilityWin::QueryService( if (!view_) return E_FAIL; - if (riid == IID_IAccessible2) + if (riid == IID_IAccessible2 || riid == IID_IAccessible2_2) AccessibleWebViewRegistry::GetInstance()->EnableIAccessible2Support(); if (guidService == IID_IAccessible || guidService == IID_IAccessible2 || + guidService == IID_IAccessible2_2 || guidService == IID_IAccessibleText) { return QueryInterface(riid, object); } @@ -1100,13 +1160,14 @@ STDMETHODIMP NativeViewAccessibilityWin::GetPatternProvider( << " for pattern id: " << id; if (id == UIA_ValuePatternId || id == UIA_TextPatternId) { - ui::AccessibleViewState state; + ui::AXViewState state; view_->GetAccessibleState(&state); long role = MSAARole(state.role); if (role == ROLE_SYSTEM_TEXT) { DVLOG(1) << "Returning UIA text provider"; - base::win::UIATextProvider::CreateTextProvider(true, provider); + base::win::UIATextProvider::CreateTextProvider( + state.value, true, provider); return S_OK; } } @@ -1120,7 +1181,7 @@ STDMETHODIMP NativeViewAccessibilityWin::GetPropertyValue(PROPERTYID id, << " for property id: " << id; if (id == UIA_ControlTypePropertyId) { - ui::AccessibleViewState state; + ui::AXViewState state; view_->GetAccessibleState(&state); long role = MSAARole(state.role); if (role == ROLE_SYSTEM_TEXT) { @@ -1149,27 +1210,25 @@ void NativeViewAccessibility::UnregisterWebView(View* web_view) { AccessibleWebViewRegistry::GetInstance()->UnregisterWebView(web_view); } -int32 NativeViewAccessibilityWin::MSAAEvent(AccessibilityTypes::Event event) { +int32 NativeViewAccessibilityWin::MSAAEvent(ui::AXEvent event) { switch (event) { - case AccessibilityTypes::EVENT_ALERT: + case ui::AX_EVENT_ALERT: return EVENT_SYSTEM_ALERT; - case AccessibilityTypes::EVENT_FOCUS: + case ui::AX_EVENT_FOCUS: return EVENT_OBJECT_FOCUS; - case AccessibilityTypes::EVENT_MENUSTART: + case ui::AX_EVENT_MENU_START: return EVENT_SYSTEM_MENUSTART; - case AccessibilityTypes::EVENT_MENUEND: + case ui::AX_EVENT_MENU_END: return EVENT_SYSTEM_MENUEND; - case AccessibilityTypes::EVENT_MENUPOPUPSTART: + case ui::AX_EVENT_MENU_POPUP_START: return EVENT_SYSTEM_MENUPOPUPSTART; - case AccessibilityTypes::EVENT_MENUPOPUPEND: + case ui::AX_EVENT_MENU_POPUP_END: return EVENT_SYSTEM_MENUPOPUPEND; - case AccessibilityTypes::EVENT_NAME_CHANGED: + case ui::AX_EVENT_TEXT_CHANGED: return EVENT_OBJECT_NAMECHANGE; - case AccessibilityTypes::EVENT_TEXT_CHANGED: - return EVENT_OBJECT_VALUECHANGE; - case AccessibilityTypes::EVENT_SELECTION_CHANGED: + case ui::AX_EVENT_SELECTION_CHANGED: return IA2_EVENT_TEXT_CARET_MOVED; - case AccessibilityTypes::EVENT_VALUE_CHANGED: + case ui::AX_EVENT_VALUE_CHANGED: return EVENT_OBJECT_VALUECHANGE; default: // Not supported or invalid event. @@ -1178,109 +1237,109 @@ int32 NativeViewAccessibilityWin::MSAAEvent(AccessibilityTypes::Event event) { } } -int32 NativeViewAccessibilityWin::MSAARole(AccessibilityTypes::Role role) { +int32 NativeViewAccessibilityWin::MSAARole(ui::AXRole role) { switch (role) { - case AccessibilityTypes::ROLE_ALERT: + case ui::AX_ROLE_ALERT: return ROLE_SYSTEM_ALERT; - case AccessibilityTypes::ROLE_APPLICATION: + case ui::AX_ROLE_APPLICATION: return ROLE_SYSTEM_APPLICATION; - case AccessibilityTypes::ROLE_BUTTONDROPDOWN: + case ui::AX_ROLE_BUTTON_DROP_DOWN: return ROLE_SYSTEM_BUTTONDROPDOWN; - case AccessibilityTypes::ROLE_BUTTONMENU: + case ui::AX_ROLE_POP_UP_BUTTON: return ROLE_SYSTEM_BUTTONMENU; - case AccessibilityTypes::ROLE_CHECKBUTTON: + case ui::AX_ROLE_CHECK_BOX: return ROLE_SYSTEM_CHECKBUTTON; - case AccessibilityTypes::ROLE_COMBOBOX: + case ui::AX_ROLE_COMBO_BOX: return ROLE_SYSTEM_COMBOBOX; - case AccessibilityTypes::ROLE_DIALOG: + case ui::AX_ROLE_DIALOG: return ROLE_SYSTEM_DIALOG; - case AccessibilityTypes::ROLE_GRAPHIC: - return ROLE_SYSTEM_GRAPHIC; - case AccessibilityTypes::ROLE_GROUPING: + case ui::AX_ROLE_GROUP: return ROLE_SYSTEM_GROUPING; - case AccessibilityTypes::ROLE_LINK: + case ui::AX_ROLE_IMAGE: + return ROLE_SYSTEM_GRAPHIC; + case ui::AX_ROLE_LINK: return ROLE_SYSTEM_LINK; - case AccessibilityTypes::ROLE_LOCATION_BAR: + case ui::AX_ROLE_LOCATION_BAR: return ROLE_SYSTEM_GROUPING; - case AccessibilityTypes::ROLE_MENUBAR: + case ui::AX_ROLE_MENU_BAR: return ROLE_SYSTEM_MENUBAR; - case AccessibilityTypes::ROLE_MENUITEM: + case ui::AX_ROLE_MENU_ITEM: return ROLE_SYSTEM_MENUITEM; - case AccessibilityTypes::ROLE_MENUPOPUP: + case ui::AX_ROLE_MENU_LIST_POPUP: return ROLE_SYSTEM_MENUPOPUP; - case AccessibilityTypes::ROLE_OUTLINE: + case ui::AX_ROLE_TREE: return ROLE_SYSTEM_OUTLINE; - case AccessibilityTypes::ROLE_OUTLINEITEM: + case ui::AX_ROLE_TREE_ITEM: return ROLE_SYSTEM_OUTLINEITEM; - case AccessibilityTypes::ROLE_PAGETAB: + case ui::AX_ROLE_TAB: return ROLE_SYSTEM_PAGETAB; - case AccessibilityTypes::ROLE_PAGETABLIST: + case ui::AX_ROLE_TAB_LIST: return ROLE_SYSTEM_PAGETABLIST; - case AccessibilityTypes::ROLE_PANE: + case ui::AX_ROLE_PANE: return ROLE_SYSTEM_PANE; - case AccessibilityTypes::ROLE_PROGRESSBAR: + case ui::AX_ROLE_PROGRESS_INDICATOR: return ROLE_SYSTEM_PROGRESSBAR; - case AccessibilityTypes::ROLE_PUSHBUTTON: + case ui::AX_ROLE_BUTTON: return ROLE_SYSTEM_PUSHBUTTON; - case AccessibilityTypes::ROLE_RADIOBUTTON: + case ui::AX_ROLE_RADIO_BUTTON: return ROLE_SYSTEM_RADIOBUTTON; - case AccessibilityTypes::ROLE_SCROLLBAR: + case ui::AX_ROLE_SCROLL_BAR: return ROLE_SYSTEM_SCROLLBAR; - case AccessibilityTypes::ROLE_SEPARATOR: + case ui::AX_ROLE_SPLITTER: return ROLE_SYSTEM_SEPARATOR; - case AccessibilityTypes::ROLE_SLIDER: + case ui::AX_ROLE_SLIDER: return ROLE_SYSTEM_SLIDER; - case AccessibilityTypes::ROLE_STATICTEXT: + case ui::AX_ROLE_STATIC_TEXT: return ROLE_SYSTEM_STATICTEXT; - case AccessibilityTypes::ROLE_TEXT: + case ui::AX_ROLE_TEXT_FIELD: return ROLE_SYSTEM_TEXT; - case AccessibilityTypes::ROLE_TITLEBAR: + case ui::AX_ROLE_TITLE_BAR: return ROLE_SYSTEM_TITLEBAR; - case AccessibilityTypes::ROLE_TOOLBAR: + case ui::AX_ROLE_TOOLBAR: return ROLE_SYSTEM_TOOLBAR; - case AccessibilityTypes::ROLE_WINDOW: + case ui::AX_ROLE_WINDOW: return ROLE_SYSTEM_WINDOW; - case AccessibilityTypes::ROLE_CLIENT: + case ui::AX_ROLE_CLIENT: default: // This is the default role for MSAA. return ROLE_SYSTEM_CLIENT; } } -int32 NativeViewAccessibilityWin::MSAAState(AccessibilityTypes::State state) { +int32 NativeViewAccessibilityWin::MSAAState(const ui::AXViewState& state) { // This maps MSAA states for get_accState(). See also the IAccessible2 // interface get_states(). int32 msaa_state = 0; - if (state & AccessibilityTypes::STATE_CHECKED) + if (state.HasStateFlag(ui::AX_STATE_CHECKED)) msaa_state |= STATE_SYSTEM_CHECKED; - if (state & AccessibilityTypes::STATE_COLLAPSED) + if (state.HasStateFlag(ui::AX_STATE_COLLAPSED)) msaa_state |= STATE_SYSTEM_COLLAPSED; - if (state & AccessibilityTypes::STATE_DEFAULT) + if (state.HasStateFlag(ui::AX_STATE_DEFAULT)) msaa_state |= STATE_SYSTEM_DEFAULT; - if (state & AccessibilityTypes::STATE_EXPANDED) + if (state.HasStateFlag(ui::AX_STATE_EXPANDED)) msaa_state |= STATE_SYSTEM_EXPANDED; - if (state & AccessibilityTypes::STATE_HASPOPUP) + if (state.HasStateFlag(ui::AX_STATE_HASPOPUP)) msaa_state |= STATE_SYSTEM_HASPOPUP; - if (state & AccessibilityTypes::STATE_HOTTRACKED) + if (state.HasStateFlag(ui::AX_STATE_HOVERED)) msaa_state |= STATE_SYSTEM_HOTTRACKED; - if (state & AccessibilityTypes::STATE_INVISIBLE) + if (state.HasStateFlag(ui::AX_STATE_INVISIBLE)) msaa_state |= STATE_SYSTEM_INVISIBLE; - if (state & AccessibilityTypes::STATE_LINKED) + if (state.HasStateFlag(ui::AX_STATE_LINKED)) msaa_state |= STATE_SYSTEM_LINKED; - if (state & AccessibilityTypes::STATE_OFFSCREEN) + if (state.HasStateFlag(ui::AX_STATE_OFFSCREEN)) msaa_state |= STATE_SYSTEM_OFFSCREEN; - if (state & AccessibilityTypes::STATE_PRESSED) + if (state.HasStateFlag(ui::AX_STATE_PRESSED)) msaa_state |= STATE_SYSTEM_PRESSED; - if (state & AccessibilityTypes::STATE_PROTECTED) + if (state.HasStateFlag(ui::AX_STATE_PROTECTED)) msaa_state |= STATE_SYSTEM_PROTECTED; - if (state & AccessibilityTypes::STATE_READONLY) + if (state.HasStateFlag(ui::AX_STATE_READ_ONLY)) msaa_state |= STATE_SYSTEM_READONLY; - if (state & AccessibilityTypes::STATE_SELECTED) + if (state.HasStateFlag(ui::AX_STATE_SELECTED)) msaa_state |= STATE_SYSTEM_SELECTED; - if (state & AccessibilityTypes::STATE_FOCUSED) + if (state.HasStateFlag(ui::AX_STATE_FOCUSED)) msaa_state |= STATE_SYSTEM_FOCUSED; - if (state & AccessibilityTypes::STATE_UNAVAILABLE) + if (state.HasStateFlag(ui::AX_STATE_DISABLED)) msaa_state |= STATE_SYSTEM_UNAVAILABLE; return msaa_state; } @@ -1339,22 +1398,22 @@ void NativeViewAccessibilityWin::SetState( msaa_state->lVal |= STATE_SYSTEM_FOCUSED; // Add on any view-specific states. - ui::AccessibleViewState view_state; + ui::AXViewState view_state; view->GetAccessibleState(&view_state); - msaa_state->lVal |= MSAAState(view_state.state); + msaa_state->lVal |= MSAAState(view_state); } -string16 NativeViewAccessibilityWin::TextForIAccessibleText() { - ui::AccessibleViewState state; +base::string16 NativeViewAccessibilityWin::TextForIAccessibleText() { + ui::AXViewState state; view_->GetAccessibleState(&state); - if (state.role == AccessibilityTypes::ROLE_TEXT) + if (state.role == ui::AX_ROLE_TEXT_FIELD) return state.value; else return state.name; } void NativeViewAccessibilityWin::HandleSpecialTextOffset( - const string16& text, LONG* offset) { + const base::string16& text, LONG* offset) { if (*offset == IA2_TEXT_OFFSET_LENGTH) { *offset = static_cast<LONG>(text.size()); } else if (*offset == IA2_TEXT_OFFSET_CARET) { @@ -1378,7 +1437,7 @@ ui::TextBoundaryType NativeViewAccessibilityWin::IA2TextBoundaryToTextBoundary( } LONG NativeViewAccessibilityWin::FindBoundary( - const string16& text, + const base::string16& text, IA2TextBoundaryType ia2_boundary, LONG start_offset, ui::TextBoundaryDirection direction) { @@ -1414,4 +1473,32 @@ void NativeViewAccessibilityWin::PopulateChildWidgetVector( } } +void NativeViewAccessibilityWin::AddAlertTarget() { + ViewStorage* view_storage = ViewStorage::GetInstance(); + for (size_t i = 0; i < alert_target_view_storage_ids_.size(); ++i) { + int view_storage_id = alert_target_view_storage_ids_[i]; + View* view = view_storage->RetrieveView(view_storage_id); + if (view == view_) + return; + } + int view_storage_id = view_storage->CreateStorageID(); + view_storage->StoreView(view_storage_id, view_); + alert_target_view_storage_ids_.push_back(view_storage_id); +} + +void NativeViewAccessibilityWin::RemoveAlertTarget() { + ViewStorage* view_storage = ViewStorage::GetInstance(); + size_t i = 0; + while (i < alert_target_view_storage_ids_.size()) { + int view_storage_id = alert_target_view_storage_ids_[i]; + View* view = view_storage->RetrieveView(view_storage_id); + if (view == NULL || view == view_) { + alert_target_view_storage_ids_.erase( + alert_target_view_storage_ids_.begin() + i); + } else { + ++i; + } + } +} + } // namespace views diff --git a/chromium/ui/views/accessibility/native_view_accessibility_win.h b/chromium/ui/views/accessibility/native_view_accessibility_win.h index 83d03dbe5d8..ef593612345 100644 --- a/chromium/ui/views/accessibility/native_view_accessibility_win.h +++ b/chromium/ui/views/accessibility/native_view_accessibility_win.h @@ -12,9 +12,10 @@ #include <UIAutomationCore.h> #include <set> +#include <vector> #include "third_party/iaccessible2/ia2_api_all.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/views/accessibility/native_view_accessibility.h" #include "ui/views/controls/native/native_view_host.h" #include "ui/views/view.h" @@ -38,7 +39,7 @@ namespace views { class __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2")) NativeViewAccessibilityWin : public CComObjectRootEx<CComMultiThreadModel>, - public IDispatchImpl<IAccessible2, &IID_IAccessible2, + public IDispatchImpl<IAccessible2_2, &IID_IAccessible2_2, &LIBID_IAccessible2Lib>, public IAccessibleText, public IServiceProvider, @@ -47,20 +48,21 @@ NativeViewAccessibilityWin public NativeViewAccessibility { public: BEGIN_COM_MAP(NativeViewAccessibilityWin) - COM_INTERFACE_ENTRY2(IDispatch, IAccessible2) - COM_INTERFACE_ENTRY2(IAccessible, IAccessible2) + COM_INTERFACE_ENTRY2(IDispatch, IAccessible2_2) + COM_INTERFACE_ENTRY(IAccessible) COM_INTERFACE_ENTRY(IAccessible2) - COM_INTERFACE_ENTRY(IAccessibleText) - COM_INTERFACE_ENTRY(IServiceProvider) + COM_INTERFACE_ENTRY(IAccessible2_2) COM_INTERFACE_ENTRY(IAccessibleEx) + COM_INTERFACE_ENTRY(IAccessibleText) COM_INTERFACE_ENTRY(IRawElementProviderSimple) + COM_INTERFACE_ENTRY(IServiceProvider) END_COM_MAP() virtual ~NativeViewAccessibilityWin(); // NativeViewAccessibility. virtual void NotifyAccessibilityEvent( - ui::AccessibilityTypes::Event event_type) OVERRIDE; + ui::AXEvent event_type) OVERRIDE; virtual gfx::NativeViewAccessible GetNativeObject() OVERRIDE; virtual void Destroy() OVERRIDE; @@ -145,6 +147,11 @@ NativeViewAccessibilityWin STDMETHODIMP get_windowHandle(HWND* window_handle); + STDMETHODIMP get_relationTargetsOfType(BSTR type, + long max_targets, + IUnknown ***targets, + long *n_targets); + // // IAccessible2 methods not implemented. // @@ -152,6 +159,9 @@ NativeViewAccessibilityWin STDMETHODIMP get_attributes(BSTR* attributes) { return E_NOTIMPL; } + STDMETHODIMP get_attribute(BSTR name, VARIANT* attribute) { + return E_NOTIMPL; + } STDMETHODIMP get_indexInParent(LONG* index_in_parent) { return E_NOTIMPL; } @@ -205,6 +215,10 @@ NativeViewAccessibilityWin STDMETHODIMP get_locale(IA2Locale* locale) { return E_NOTIMPL; } + STDMETHODIMP get_accessibleWithCaret(IUnknown** accessible, + long* caret_offset) { + return E_NOTIMPL; + } // // IAccessibleText methods. @@ -337,17 +351,17 @@ NativeViewAccessibilityWin // Static methods - // Returns a conversion from the event (as defined in accessibility_types.h) + // Returns a conversion from the event (as defined in ax_enums.idl) // to an MSAA event. - static int32 MSAAEvent(ui::AccessibilityTypes::Event event); + static int32 MSAAEvent(ui::AXEvent event); - // Returns a conversion from the Role (as defined in accessibility_types.h) + // Returns a conversion from the Role (as defined in ax_enums.idl) // to an MSAA role. - static int32 MSAARole(ui::AccessibilityTypes::Role role); + static int32 MSAARole(ui::AXRole role); - // Returns a conversion from the State (as defined in accessibility_types.h) + // Returns a conversion from the State (as defined in ax_enums.idl) // to MSAA states set. - static int32 MSAAState(ui::AccessibilityTypes::State state); + static int32 MSAAState(const ui::AXViewState& state); protected: NativeViewAccessibilityWin(); @@ -374,11 +388,11 @@ NativeViewAccessibilityWin void SetState(VARIANT* msaa_state, View* view); // Return the text to use for IAccessibleText. - string16 TextForIAccessibleText(); + base::string16 TextForIAccessibleText(); // If offset is a member of IA2TextSpecialOffsets this function updates the // value of offset and returns, otherwise offset remains unchanged. - void HandleSpecialTextOffset(const string16& text, LONG* offset); + void HandleSpecialTextOffset(const base::string16& text, LONG* offset); // Convert from a IA2TextBoundaryType to a ui::TextBoundaryType. ui::TextBoundaryType IA2TextBoundaryToTextBoundary(IA2TextBoundaryType type); @@ -386,7 +400,7 @@ NativeViewAccessibilityWin // Search forwards (direction == 1) or backwards (direction == -1) // from the given offset until the given boundary is found, and // return the offset of that boundary. - LONG FindBoundary(const string16& text, + LONG FindBoundary(const base::string16& text, IA2TextBoundaryType ia2_boundary, LONG start_offset, ui::TextBoundaryDirection direction); @@ -396,6 +410,12 @@ NativeViewAccessibilityWin // NativeViewHost. void PopulateChildWidgetVector(std::vector<Widget*>* child_widgets); + // Adds this view to alert_target_view_storage_ids_. + void AddAlertTarget(); + + // Removes this view from alert_target_view_storage_ids_. + void RemoveAlertTarget(); + // Give CComObject access to the class constructor. template <class Base> friend class CComObject; @@ -418,6 +438,11 @@ NativeViewAccessibilityWin // Next index into |view_storage_ids_| to use. static int next_view_storage_id_index_; + // A vector of view storage ids of views that have been the target of + // an alert event, in order to provide an api to quickly identify all + // open alerts. + static std::vector<int> alert_target_view_storage_ids_; + DISALLOW_COPY_AND_ASSIGN(NativeViewAccessibilityWin); }; diff --git a/chromium/ui/views/accessibility/native_view_accessibility_win_unittest.cc b/chromium/ui/views/accessibility/native_view_accessibility_win_unittest.cc index 5bd5bed7b91..3edab8e258e 100644 --- a/chromium/ui/views/accessibility/native_view_accessibility_win_unittest.cc +++ b/chromium/ui/views/accessibility/native_view_accessibility_win_unittest.cc @@ -7,14 +7,33 @@ #include "base/win/scoped_bstr.h" #include "base/win/scoped_comptr.h" #include "base/win/scoped_variant.h" +#include "third_party/iaccessible2/ia2_api_all.h" #include "ui/views/accessibility/native_view_accessibility.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/test/views_test_base.h" +using base::win::ScopedBstr; +using base::win::ScopedComPtr; +using base::win::ScopedVariant; + namespace views { namespace test { -typedef ViewsTestBase NativeViewAcccessibilityWinTest; +class NativeViewAcccessibilityWinTest : public ViewsTestBase { + public: + NativeViewAcccessibilityWinTest() {} + virtual ~NativeViewAcccessibilityWinTest() {} + + protected: + void GetIAccessible2InterfaceForView(View* view, IAccessible2_2** result) { + ScopedComPtr<IAccessible> view_accessible( + view->GetNativeViewAccessible()); + ScopedComPtr<IServiceProvider> service_provider; + ASSERT_EQ(S_OK, view_accessible.QueryInterface(service_provider.Receive())); + ASSERT_EQ(S_OK, + service_provider->QueryService(IID_IAccessible2_2, result)); + } +}; TEST_F(NativeViewAcccessibilityWinTest, TextfieldAccessibility) { Widget widget; @@ -31,32 +50,32 @@ TEST_F(NativeViewAcccessibilityWinTest, TextfieldAccessibility) { textfield->SetText(L"Value"); content->AddChildView(textfield); - base::win::ScopedComPtr<IAccessible> content_accessible( + ScopedComPtr<IAccessible> content_accessible( content->GetNativeViewAccessible()); LONG child_count = 0; ASSERT_EQ(S_OK, content_accessible->get_accChildCount(&child_count)); ASSERT_EQ(1L, child_count); - base::win::ScopedComPtr<IDispatch> textfield_dispatch; - base::win::ScopedComPtr<IAccessible> textfield_accessible; - base::win::ScopedVariant child_index(1); + ScopedComPtr<IDispatch> textfield_dispatch; + ScopedComPtr<IAccessible> textfield_accessible; + ScopedVariant child_index(1); ASSERT_EQ(S_OK, content_accessible->get_accChild( child_index, textfield_dispatch.Receive())); ASSERT_EQ(S_OK, textfield_dispatch.QueryInterface( textfield_accessible.Receive())); - base::win::ScopedBstr name; - base::win::ScopedVariant childid_self(CHILDID_SELF); + ScopedBstr name; + ScopedVariant childid_self(CHILDID_SELF); ASSERT_EQ(S_OK, textfield_accessible->get_accName( childid_self, name.Receive())); ASSERT_STREQ(L"Name", name); - base::win::ScopedBstr value; + ScopedBstr value; ASSERT_EQ(S_OK, textfield_accessible->get_accValue( childid_self, value.Receive())); ASSERT_STREQ(L"Value", value); - base::win::ScopedBstr new_value(L"New value"); + ScopedBstr new_value(L"New value"); ASSERT_EQ(S_OK, textfield_accessible->put_accValue(childid_self, new_value)); ASSERT_STREQ(L"New value", textfield->text().c_str()); @@ -81,17 +100,16 @@ TEST_F(NativeViewAcccessibilityWinTest, UnattachedWebView) { content->AddChildView(web_view); NativeViewAccessibility::RegisterWebView(web_view); - base::win::ScopedComPtr<IAccessible> web_view_accessible( + ScopedComPtr<IAccessible> web_view_accessible( web_view->GetNativeViewAccessible()); - base::win::ScopedComPtr<IDispatch> result_dispatch; - base::win::ScopedVariant child_index(-999); + ScopedComPtr<IDispatch> result_dispatch; + ScopedVariant child_index(-999); ASSERT_EQ(E_FAIL, web_view_accessible->get_accChild( child_index, result_dispatch.Receive())); NativeViewAccessibility::UnregisterWebView(web_view); } -#if defined(USE_AURA) TEST_F(NativeViewAcccessibilityWinTest, AuraOwnedWidgets) { Widget widget; Widget::InitParams init_params = @@ -99,7 +117,7 @@ TEST_F(NativeViewAcccessibilityWinTest, AuraOwnedWidgets) { init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; widget.Init(init_params); - base::win::ScopedComPtr<IAccessible> root_view_accessible( + ScopedComPtr<IAccessible> root_view_accessible( widget.GetRootView()->GetNativeViewAccessible()); LONG child_count = 0; @@ -110,7 +128,6 @@ TEST_F(NativeViewAcccessibilityWinTest, AuraOwnedWidgets) { Widget::InitParams owned_init_params = CreateParams(Widget::InitParams::TYPE_POPUP); owned_init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - owned_init_params.child = false; owned_init_params.parent = widget.GetNativeView(); owned_widget.Init(owned_init_params); owned_widget.Show(); @@ -118,7 +135,70 @@ TEST_F(NativeViewAcccessibilityWinTest, AuraOwnedWidgets) { ASSERT_EQ(S_OK, root_view_accessible->get_accChildCount(&child_count)); ASSERT_EQ(2L, child_count); } -#endif + +TEST_F(NativeViewAcccessibilityWinTest, RetrieveAllAlerts) { + Widget widget; + Widget::InitParams init_params = + CreateParams(Widget::InitParams::TYPE_POPUP); + init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget.Init(init_params); + + View* content = new View; + widget.SetContentsView(content); + + View* infobar = new View; + content->AddChildView(infobar); + + View* infobar2 = new View; + content->AddChildView(infobar2); + + View* root_view = content->parent(); + ASSERT_EQ(NULL, root_view->parent()); + + ScopedComPtr<IAccessible2_2> root_view_accessible; + GetIAccessible2InterfaceForView(root_view, root_view_accessible.Receive()); + + ScopedComPtr<IAccessible2_2> infobar_accessible; + GetIAccessible2InterfaceForView(infobar, infobar_accessible.Receive()); + + ScopedComPtr<IAccessible2_2> infobar2_accessible; + GetIAccessible2InterfaceForView(infobar2, infobar2_accessible.Receive()); + + // Initially, there are no alerts + ScopedBstr alerts_bstr(L"alerts"); + IUnknown** targets; + long n_targets; + ASSERT_EQ(S_FALSE, root_view_accessible->get_relationTargetsOfType( + alerts_bstr, 0, &targets, &n_targets)); + ASSERT_EQ(0, n_targets); + + // Fire alert events on the infobars. + infobar->NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true); + infobar2->NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true); + + // Now calling get_relationTargetsOfType should retrieve the alerts. + ASSERT_EQ(S_OK, root_view_accessible->get_relationTargetsOfType( + alerts_bstr, 0, &targets, &n_targets)); + ASSERT_EQ(2, n_targets); + ASSERT_TRUE(infobar_accessible.IsSameObject(targets[0])); + ASSERT_TRUE(infobar2_accessible.IsSameObject(targets[1])); + CoTaskMemFree(targets); + + // If we set max_targets to 1, we should only get the first one. + ASSERT_EQ(S_OK, root_view_accessible->get_relationTargetsOfType( + alerts_bstr, 1, &targets, &n_targets)); + ASSERT_EQ(1, n_targets); + ASSERT_TRUE(infobar_accessible.IsSameObject(targets[0])); + CoTaskMemFree(targets); + + // If we delete the first view, we should only get the second one now. + delete infobar; + ASSERT_EQ(S_OK, root_view_accessible->get_relationTargetsOfType( + alerts_bstr, 0, &targets, &n_targets)); + ASSERT_EQ(1, n_targets); + ASSERT_TRUE(infobar2_accessible.IsSameObject(targets[0])); + CoTaskMemFree(targets); +} } // namespace test } // namespace views diff --git a/chromium/ui/views/accessible_pane_view.cc b/chromium/ui/views/accessible_pane_view.cc index 35ca7bf6e83..a0bbd5665fc 100644 --- a/chromium/ui/views/accessible_pane_view.cc +++ b/chromium/ui/views/accessible_pane_view.cc @@ -5,7 +5,7 @@ #include "ui/views/accessible_pane_view.h" #include "base/message_loop/message_loop.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/views/focus/focus_search.h" #include "ui/views/focus/view_storage.h" #include "ui/views/widget/widget.h" @@ -207,8 +207,8 @@ void AccessiblePaneView::SetVisible(bool flag) { View::SetVisible(flag); } -void AccessiblePaneView::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_PANE; +void AccessiblePaneView::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_PANE; } void AccessiblePaneView::RequestFocus() { diff --git a/chromium/ui/views/accessible_pane_view.h b/chromium/ui/views/accessible_pane_view.h index b3ab9b770e1..cde84a75243 100644 --- a/chromium/ui/views/accessible_pane_view.h +++ b/chromium/ui/views/accessible_pane_view.h @@ -44,7 +44,7 @@ class VIEWS_EXPORT AccessiblePaneView : public View, virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE; virtual void SetVisible(bool flag) OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; virtual void RequestFocus() OVERRIDE; // Overridden from FocusChangeListener: diff --git a/chromium/ui/views/accessible_pane_view_unittest.cc b/chromium/ui/views/accessible_pane_view_unittest.cc index 02778d6beb5..4468f2ffe3e 100644 --- a/chromium/ui/views/accessible_pane_view_unittest.cc +++ b/chromium/ui/views/accessible_pane_view_unittest.cc @@ -57,7 +57,7 @@ void TestBarView::ButtonPressed(Button* sender, const ui::Event& event) { void TestBarView::Init() { SetLayoutManager(new FillLayout()); - string16 label; + base::string16 label; child_button_.reset(new LabelButton(this, label)); AddChildView(child_button_.get()); second_child_button_.reset(new LabelButton(this, label)); @@ -101,7 +101,6 @@ TEST_F(AccessiblePaneViewTest, SimpleSetPaneFocus) { // This test will not work properly in Windows because it uses ::GetNextWindow // on deactivate which is rather unpredictable where the focus will land. -#if !defined(OS_WIN)||defined(USE_AURA) TEST_F(AccessiblePaneViewTest, SetPaneFocusAndRestore) { View* test_view_main = new View(); scoped_ptr<Widget> widget_main(new Widget()); @@ -145,7 +144,6 @@ TEST_F(AccessiblePaneViewTest, SetPaneFocusAndRestore) { widget_main->CloseNow(); widget_main.reset(); } -#endif // !defined(OS_WIN)||defined(USE_AURA) TEST_F(AccessiblePaneViewTest, TwoSetPaneFocus) { TestBarView* test_view = new TestBarView(); diff --git a/chromium/ui/views/animation/bounds_animator.cc b/chromium/ui/views/animation/bounds_animator.cc index 839294ff74c..ba9412722e0 100644 --- a/chromium/ui/views/animation/bounds_animator.cc +++ b/chromium/ui/views/animation/bounds_animator.cc @@ -110,13 +110,12 @@ const SlideAnimation* BoundsAnimator::GetAnimationForView(View* view) { return !IsAnimating(view) ? NULL : data_[view].animation; } -void BoundsAnimator::SetAnimationDelegate(View* view, - AnimationDelegate* delegate, - bool delete_when_done) { +void BoundsAnimator::SetAnimationDelegate( + View* view, + scoped_ptr<AnimationDelegate> delegate) { DCHECK(IsAnimating(view)); - data_[view].delegate = delegate; - data_[view].delete_delegate_when_done = delete_when_done; + data_[view].delegate = delegate.release(); } void BoundsAnimator::StopAnimatingView(View* view) { @@ -177,10 +176,8 @@ void BoundsAnimator::CleanupData(bool send_cancel, Data* data, View* view) { if (send_cancel && data->delegate) data->delegate->AnimationCanceled(data->animation); - if (data->delete_delegate_when_done) { - delete static_cast<OwnedAnimationDelegate*>(data->delegate); - data->delegate = NULL; - } + delete data->delegate; + data->delegate = NULL; if (data->animation) { data->animation->set_delegate(NULL); diff --git a/chromium/ui/views/animation/bounds_animator.h b/chromium/ui/views/animation/bounds_animator.h index 1688dd56235..1a88cdf0575 100644 --- a/chromium/ui/views/animation/bounds_animator.h +++ b/chromium/ui/views/animation/bounds_animator.h @@ -38,13 +38,6 @@ class View; class VIEWS_EXPORT BoundsAnimator : public gfx::AnimationDelegate, public gfx::AnimationContainerObserver { public: - // If |delete_when_done| is set to true in |SetAnimationDelegate| the - // |AnimationDelegate| must subclass this class. - class OwnedAnimationDelegate : public gfx::AnimationDelegate { - public: - virtual ~OwnedAnimationDelegate() {} - }; - explicit BoundsAnimator(View* view); virtual ~BoundsAnimator(); @@ -74,12 +67,9 @@ class VIEWS_EXPORT BoundsAnimator : public gfx::AnimationDelegate, // Stops animating the specified view. void StopAnimatingView(View* view); - // Sets the delegate for the animation created for the specified view. If - // |delete_when_done| is true the |delegate| is deleted when done and - // |delegate| must subclass OwnedAnimationDelegate. + // Sets the delegate for the animation for the specified view. void SetAnimationDelegate(View* view, - gfx::AnimationDelegate* delegate, - bool delete_when_done); + scoped_ptr<gfx::AnimationDelegate> delegate); // Returns true if BoundsAnimator is animating the bounds of |view|. bool IsAnimating(View* view) const; @@ -95,6 +85,9 @@ class VIEWS_EXPORT BoundsAnimator : public gfx::AnimationDelegate, // milliseconds. void SetAnimationDuration(int duration_ms); + // Gets the currently used animation duration. + int GetAnimationDuration() const { return animation_duration_ms_; } + // Sets the tween type for new animations. Default is EASE_OUT. void set_tween_type(gfx::Tween::Type type) { tween_type_ = type; } @@ -108,13 +101,7 @@ class VIEWS_EXPORT BoundsAnimator : public gfx::AnimationDelegate, private: // Tracks data about the view being animated. struct Data { - Data() - : delete_delegate_when_done(false), - animation(NULL), - delegate(NULL) {} - - // If true the delegate is deleted when done. - bool delete_delegate_when_done; + Data() : animation(NULL), delegate(NULL) {} // The initial bounds. gfx::Rect start_bounds; @@ -125,7 +112,7 @@ class VIEWS_EXPORT BoundsAnimator : public gfx::AnimationDelegate, // The animation. We own this. gfx::SlideAnimation* animation; - // Additional delegate for the animation, may be null. + // Delegate for the animation, may be null. We own this. gfx::AnimationDelegate* delegate; }; diff --git a/chromium/ui/views/animation/bounds_animator_unittest.cc b/chromium/ui/views/animation/bounds_animator_unittest.cc index 0928fe665b0..6294d8a5912 100644 --- a/chromium/ui/views/animation/bounds_animator_unittest.cc +++ b/chromium/ui/views/animation/bounds_animator_unittest.cc @@ -32,7 +32,7 @@ class TestBoundsAnimator : public BoundsAnimator { DISALLOW_COPY_AND_ASSIGN(TestBoundsAnimator); }; -class OwnedDelegate : public BoundsAnimator::OwnedAnimationDelegate { +class OwnedDelegate : public gfx::AnimationDelegate { public: OwnedDelegate() {} @@ -110,12 +110,12 @@ class BoundsAnimatorTest : public testing::Test { // Checks animate view to. TEST_F(BoundsAnimatorTest, AnimateViewTo) { - TestAnimationDelegate delegate; gfx::Rect initial_bounds(0, 0, 10, 10); child()->SetBoundsRect(initial_bounds); gfx::Rect target_bounds(10, 10, 20, 20); animator()->AnimateViewTo(child(), target_bounds); - animator()->SetAnimationDelegate(child(), &delegate, false); + animator()->SetAnimationDelegate( + child(), scoped_ptr<gfx::AnimationDelegate>(new TestAnimationDelegate())); // The animator should be animating now. EXPECT_TRUE(animator()->IsAnimating()); @@ -136,7 +136,8 @@ TEST_F(BoundsAnimatorTest, AnimateViewTo) { // Make sure an AnimationDelegate is deleted when canceled. TEST_F(BoundsAnimatorTest, DeleteDelegateOnCancel) { animator()->AnimateViewTo(child(), gfx::Rect(0, 0, 10, 10)); - animator()->SetAnimationDelegate(child(), new OwnedDelegate(), true); + animator()->SetAnimationDelegate( + child(), scoped_ptr<gfx::AnimationDelegate>(new OwnedDelegate())); animator()->Cancel(); @@ -152,7 +153,8 @@ TEST_F(BoundsAnimatorTest, DeleteDelegateOnCancel) { // scheduled. TEST_F(BoundsAnimatorTest, DeleteDelegateOnNewAnimate) { animator()->AnimateViewTo(child(), gfx::Rect(0, 0, 10, 10)); - animator()->SetAnimationDelegate(child(), new OwnedDelegate(), true); + animator()->SetAnimationDelegate( + child(), scoped_ptr<gfx::AnimationDelegate>(new OwnedDelegate())); animator()->AnimateViewTo(child(), gfx::Rect(0, 0, 10, 10)); @@ -166,7 +168,8 @@ TEST_F(BoundsAnimatorTest, StopAnimating) { scoped_ptr<OwnedDelegate> delegate(new OwnedDelegate()); animator()->AnimateViewTo(child(), gfx::Rect(0, 0, 10, 10)); - animator()->SetAnimationDelegate(child(), new OwnedDelegate(), true); + animator()->SetAnimationDelegate( + child(), scoped_ptr<gfx::AnimationDelegate>(new OwnedDelegate())); animator()->StopAnimatingView(child()); diff --git a/chromium/ui/views/background.cc b/chromium/ui/views/background.cc index b37318dc2b4..bb2ddd0928e 100644 --- a/chromium/ui/views/background.cc +++ b/chromium/ui/views/background.cc @@ -94,12 +94,7 @@ Background* Background::CreateSolidBackground(SkColor color) { //static Background* Background::CreateStandardPanelBackground() { // TODO(beng): Should be in NativeTheme. -#if defined(USE_AURA) return CreateSolidBackground(SK_ColorWHITE); -#else - return CreateVerticalGradientBackground(SkColorSetRGB(246, 250, 255), - SkColorSetRGB(219, 235, 255)); -#endif } //static diff --git a/chromium/ui/views/border.cc b/chromium/ui/views/border.cc index f347c2a8369..0ff340642e0 100644 --- a/chromium/ui/views/border.cc +++ b/chromium/ui/views/border.cc @@ -133,36 +133,37 @@ Border::~Border() { } // static -Border* Border::CreateSolidBorder(int thickness, SkColor color) { - return new SolidBorder(thickness, color); +scoped_ptr<Border> Border::NullBorder() { + return scoped_ptr<Border>(); } // static -Border* Border::CreateEmptyBorder(int top, int left, int bottom, int right) { - return new EmptyBorder(top, left, bottom, right); +scoped_ptr<Border> Border::CreateSolidBorder(int thickness, SkColor color) { + return scoped_ptr<Border>(new SolidBorder(thickness, color)); } // static -Border* Border::CreateSolidSidedBorder(int top, - int left, - int bottom, - int right, - SkColor color) { - return new SidedSolidBorder(top, left, bottom, right, color); +scoped_ptr<Border> Border::CreateEmptyBorder(int top, + int left, + int bottom, + int right) { + return scoped_ptr<Border>(new EmptyBorder(top, left, bottom, right)); } // static -Border* Border::CreateBorderPainter(Painter* painter, - const gfx::Insets& insets) { - return new BorderPainter(painter, insets); +scoped_ptr<Border> Border::CreateSolidSidedBorder(int top, + int left, + int bottom, + int right, + SkColor color) { + return scoped_ptr<Border>( + new SidedSolidBorder(top, left, bottom, right, color)); } -TextButtonBorder* Border::AsTextButtonBorder() { - return NULL; -} - -const TextButtonBorder* Border::AsTextButtonBorder() const { - return NULL; +// static +scoped_ptr<Border> Border::CreateBorderPainter(Painter* painter, + const gfx::Insets& insets) { + return scoped_ptr<Border>(new BorderPainter(painter, insets)); } } // namespace views diff --git a/chromium/ui/views/border.h b/chromium/ui/views/border.h index 2e9c56bf4bf..d2657c60c13 100644 --- a/chromium/ui/views/border.h +++ b/chromium/ui/views/border.h @@ -6,6 +6,7 @@ #define UI_VIEWS_BORDER_H_ #include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/insets.h" #include "ui/views/views_export.h" @@ -26,7 +27,7 @@ class View; // // The border class is used to display a border around a view. // To set a border on a view, just call SetBorder on the view, for example: -// view->set_border(Border::CreateSolidBorder(1, SkColorSetRGB(25, 25, 112)); +// view->SetBorder(Border::CreateSolidBorder(1, SkColorSetRGB(25, 25, 112)); // Once set on a view, the border is owned by the view. // // IMPORTANT NOTE: not all views support borders at this point. In order to @@ -36,34 +37,38 @@ class View; // //////////////////////////////////////////////////////////////////////////////// -class TextButtonBorder; - class VIEWS_EXPORT Border { public: Border(); virtual ~Border(); + // Convenience for creating a scoped_ptr with no Border. + static scoped_ptr<Border> NullBorder(); + // Creates a border that is a simple line of the specified thickness and // color. - static Border* CreateSolidBorder(int thickness, SkColor color); + static scoped_ptr<Border> CreateSolidBorder(int thickness, SkColor color); // Creates a border for reserving space. The returned border does not // paint anything. - static Border* CreateEmptyBorder(int top, int left, int bottom, int right); + static scoped_ptr<Border> CreateEmptyBorder(int top, + int left, + int bottom, + int right); // Creates a border of the specified color, and specified thickness on each // side. - static Border* CreateSolidSidedBorder(int top, - int left, - int bottom, - int right, - SkColor color); + static scoped_ptr<Border> CreateSolidSidedBorder(int top, + int left, + int bottom, + int right, + SkColor color); // Creates a Border from the specified Painter. The border owns the painter, // thus the painter is deleted when the Border is deleted. // |insets| define size of an area allocated for a Border. - static Border* CreateBorderPainter(Painter* painter, - const gfx::Insets& insets); + static scoped_ptr<Border> CreateBorderPainter(Painter* painter, + const gfx::Insets& insets); // Renders the border for the specified view. virtual void Paint(const View& view, gfx::Canvas* canvas) = 0; @@ -79,10 +84,6 @@ class VIEWS_EXPORT Border { // content laid out relative to these images. virtual gfx::Size GetMinimumSize() const = 0; - // Manual RTTI for text buttons. - virtual TextButtonBorder* AsTextButtonBorder(); - virtual const TextButtonBorder* AsTextButtonBorder() const; - private: DISALLOW_COPY_AND_ASSIGN(Border); }; diff --git a/chromium/ui/views/bubble/bubble_border.cc b/chromium/ui/views/bubble/bubble_border.cc index aa9064ee970..03b422aefb2 100644 --- a/chromium/ui/views/bubble/bubble_border.cc +++ b/chromium/ui/views/bubble/bubble_border.cc @@ -156,7 +156,8 @@ BubbleBorder::BubbleBorder(Arrow arrow, Shadow shadow, SkColor color) arrow_paint_type_(PAINT_NORMAL), alignment_(ALIGN_ARROW_TO_MID_ANCHOR), shadow_(shadow), - background_color_(color) { + background_color_(color), + use_theme_background_color_(false) { DCHECK(shadow < SHADOW_COUNT); images_ = GetBorderImages(shadow); } diff --git a/chromium/ui/views/bubble/bubble_border.h b/chromium/ui/views/bubble/bubble_border.h index 4fb961e6fb4..15f0f85f60d 100644 --- a/chromium/ui/views/bubble/bubble_border.h +++ b/chromium/ui/views/bubble/bubble_border.h @@ -136,6 +136,13 @@ class VIEWS_EXPORT BubbleBorder : public Border { void set_background_color(SkColor color) { background_color_ = color; } SkColor background_color() const { return background_color_; } + // If true, the background color should be determined by the host's + // NativeTheme. + void set_use_theme_background_color(bool use_theme_background_color) { + use_theme_background_color_ = use_theme_background_color; + } + bool use_theme_background_color() { return use_theme_background_color_; } + // Sets a desired pixel distance between the arrow tip and the outside edge of // the neighboring border image. For example: |----offset----| // '(' represents shadow around the '{' edge: ((({ ^ }))) @@ -178,6 +185,7 @@ class VIEWS_EXPORT BubbleBorder : public Border { Shadow shadow_; internal::BorderImages* images_; SkColor background_color_; + bool use_theme_background_color_; DISALLOW_COPY_AND_ASSIGN(BubbleBorder); }; diff --git a/chromium/ui/views/bubble/bubble_delegate.cc b/chromium/ui/views/bubble/bubble_delegate.cc index dbd0cae85fe..0eab071ad0c 100644 --- a/chromium/ui/views/bubble/bubble_delegate.cc +++ b/chromium/ui/views/bubble/bubble_delegate.cc @@ -4,8 +4,8 @@ #include "ui/views/bubble/bubble_delegate.h" -#include "ui/base/accessibility/accessible_view_state.h" -#include "ui/gfx/animation/slide_animation.h" +#include "ui/accessibility/ax_view_state.h" +#include "ui/base/resource/resource_bundle.h" #include "ui/gfx/color_utils.h" #include "ui/gfx/rect.h" #include "ui/native_theme/native_theme.h" @@ -18,9 +18,6 @@ #include "ui/base/win/shell.h" #endif -// The duration of the fade animation in milliseconds. -static const int kHideFadeDurationMS = 200; - // The defaut margin between the content and the inside border, in pixels. static const int kDefaultMargin = 6; @@ -39,7 +36,8 @@ Widget* CreateBubbleWidget(BubbleDelegateView* bubble) { bubble_params.parent = bubble->parent_window(); else if (bubble->anchor_widget()) bubble_params.parent = bubble->anchor_widget()->GetNativeView(); - bubble_params.can_activate = bubble->CanActivate(); + bubble_params.activatable = bubble->CanActivate() ? + Widget::InitParams::ACTIVATABLE_YES : Widget::InitParams::ACTIVATABLE_NO; bubble->OnBeforeBubbleWidgetInit(&bubble_params, bubble_widget); bubble_widget->Init(bubble_params); return bubble_widget; @@ -52,12 +50,10 @@ BubbleDelegateView::BubbleDelegateView() close_on_deactivate_(true), anchor_view_storage_id_(ViewStorage::GetInstance()->CreateStorageID()), anchor_widget_(NULL), - move_with_anchor_(false), arrow_(BubbleBorder::TOP_LEFT), shadow_(BubbleBorder::SMALL_SHADOW), color_explicitly_set_(false), margins_(kDefaultMargin, kDefaultMargin, kDefaultMargin, kDefaultMargin), - original_opacity_(255), use_focusless_(false), accept_events_(true), border_accepts_events_(true), @@ -74,12 +70,10 @@ BubbleDelegateView::BubbleDelegateView( close_on_deactivate_(true), anchor_view_storage_id_(ViewStorage::GetInstance()->CreateStorageID()), anchor_widget_(NULL), - move_with_anchor_(false), arrow_(arrow), shadow_(BubbleBorder::SMALL_SHADOW), color_explicitly_set_(false), margins_(kDefaultMargin, kDefaultMargin, kDefaultMargin, kDefaultMargin), - original_opacity_(255), use_focusless_(false), accept_events_(true), border_accepts_events_(true), @@ -91,6 +85,8 @@ BubbleDelegateView::BubbleDelegateView( } BubbleDelegateView::~BubbleDelegateView() { + if (GetWidget()) + GetWidget()->RemoveObserver(this); SetLayoutManager(NULL); SetAnchorView(NULL); } @@ -102,11 +98,14 @@ Widget* BubbleDelegateView::CreateBubble(BubbleDelegateView* bubble_delegate) { bubble_delegate->SetAnchorView(bubble_delegate->GetAnchorView()); Widget* bubble_widget = CreateBubbleWidget(bubble_delegate); -#if defined(OS_WIN) && defined(USE_AURA) +#if defined(OS_WIN) // If glass is enabled, the bubble is allowed to extend outside the bounds of // the parent frame and let DWM handle compositing. If not, then we don't // want to allow the bubble to extend the frame because it will be clipped. bubble_delegate->set_adjust_if_offscreen(ui::win::IsAeroGlassEnabled()); +#elif defined(OS_LINUX) && !defined(OS_CHROMEOS) + // Linux clips bubble windows that extend outside their parent window bounds. + bubble_delegate->set_adjust_if_offscreen(false); #endif bubble_delegate->SizeToContents(); @@ -133,15 +132,19 @@ View* BubbleDelegateView::GetContentsView() { NonClientFrameView* BubbleDelegateView::CreateNonClientFrameView( Widget* widget) { BubbleFrameView* frame = new BubbleFrameView(margins()); + // Note: In CreateBubble, the call to SizeToContents() will cause + // the relayout that this call requires. + frame->SetTitleFontList(GetTitleFontList()); BubbleBorder::Arrow adjusted_arrow = arrow(); if (base::i18n::IsRTL()) adjusted_arrow = BubbleBorder::horizontal_mirror(adjusted_arrow); - frame->SetBubbleBorder(new BubbleBorder(adjusted_arrow, shadow(), color())); + frame->SetBubbleBorder(scoped_ptr<BubbleBorder>( + new BubbleBorder(adjusted_arrow, shadow(), color()))); return frame; } -void BubbleDelegateView::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_DIALOG; +void BubbleDelegateView::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_DIALOG; } void BubbleDelegateView::OnWidgetDestroying(Widget* widget) { @@ -174,19 +177,15 @@ void BubbleDelegateView::OnWidgetActivationChanged(Widget* widget, void BubbleDelegateView::OnWidgetBoundsChanged(Widget* widget, const gfx::Rect& new_bounds) { - if (anchor_widget() == widget) { - if (move_with_anchor()) - SizeToContents(); - else - GetWidget()->Close(); - } + if (anchor_widget() == widget) + SizeToContents(); } View* BubbleDelegateView::GetAnchorView() const { return ViewStorage::GetInstance()->RetrieveView(anchor_view_storage_id_); } -gfx::Rect BubbleDelegateView::GetAnchorRect() { +gfx::Rect BubbleDelegateView::GetAnchorRect() const { if (!GetAnchorView()) return anchor_rect_; @@ -199,36 +198,6 @@ void BubbleDelegateView::OnBeforeBubbleWidgetInit(Widget::InitParams* params, Widget* widget) const { } -void BubbleDelegateView::StartFade(bool fade_in) { -#if defined(USE_AURA) - // Use AURA's window layer animation instead of fading. This ensures that - // hosts which rely on the layer animation callbacks to close the window - // work correctly. - if (fade_in) - GetWidget()->Show(); - else - GetWidget()->Close(); -#else - fade_animation_.reset(new gfx::SlideAnimation(this)); - fade_animation_->SetSlideDuration(GetFadeDuration()); - fade_animation_->Reset(fade_in ? 0.0 : 1.0); - if (fade_in) { - original_opacity_ = 0; - GetWidget()->SetOpacity(original_opacity_); - GetWidget()->Show(); - fade_animation_->Show(); - } else { - original_opacity_ = 255; - fade_animation_->Hide(); - } -#endif -} - -void BubbleDelegateView::ResetFade() { - fade_animation_.reset(); - GetWidget()->SetOpacity(original_opacity_); -} - void BubbleDelegateView::SetAlignment(BubbleBorder::BubbleAlignment alignment) { GetBubbleFrameView()->bubble_border()->set_alignment(alignment); SizeToContents(); @@ -248,8 +217,6 @@ bool BubbleDelegateView::AcceleratorPressed( const ui::Accelerator& accelerator) { if (!close_on_esc() || accelerator.key_code() != ui::VKEY_ESCAPE) return false; - if (fade_animation_.get()) - fade_animation_->Reset(); GetWidget()->Close(); return true; } @@ -258,22 +225,6 @@ void BubbleDelegateView::OnNativeThemeChanged(const ui::NativeTheme* theme) { UpdateColorsFromTheme(theme); } -void BubbleDelegateView::AnimationEnded(const gfx::Animation* animation) { - if (animation != fade_animation_.get()) - return; - bool closed = fade_animation_->GetCurrentValue() == 0; - fade_animation_->Reset(); - if (closed) - GetWidget()->Close(); -} - -void BubbleDelegateView::AnimationProgressed(const gfx::Animation* animation) { - if (animation != fade_animation_.get()) - return; - DCHECK(fade_animation_->is_animating()); - GetWidget()->SetOpacity(fade_animation_->GetCurrentValue() * 255); -} - void BubbleDelegateView::Init() {} void BubbleDelegateView::SetAnchorView(View* anchor_view) { @@ -298,7 +249,11 @@ void BubbleDelegateView::SetAnchorView(View* anchor_view) { if (anchor_view) view_storage->StoreView(anchor_view_storage_id_, anchor_view); - if (GetWidget()) + // Do not update anchoring for NULL views; this could indicate that our + // NativeWindow is being destroyed, so it would be dangerous for us to update + // our anchor bounds at that point. (It's safe to skip this, since if we were + // to update the bounds when |anchor_view| is NULL, the bubble won't move.) + if (anchor_view && GetWidget()) OnAnchorBoundsChanged(); } @@ -321,19 +276,20 @@ BubbleFrameView* BubbleDelegateView::GetBubbleFrameView() const { gfx::Rect BubbleDelegateView::GetBubbleBounds() { // The argument rect has its origin at the bubble's arrow anchor point; // its size is the preferred size of the bubble's client view (this view). + bool anchor_minimized = anchor_widget() && anchor_widget()->IsMinimized(); return GetBubbleFrameView()->GetUpdatedWindowBounds(GetAnchorRect(), - GetPreferredSize(), adjust_if_offscreen_); + GetPreferredSize(), adjust_if_offscreen_ && !anchor_minimized); } -int BubbleDelegateView::GetFadeDuration() { - return kHideFadeDurationMS; +const gfx::FontList& BubbleDelegateView::GetTitleFontList() const { + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + return rb.GetFontList(ui::ResourceBundle::MediumFont); } + void BubbleDelegateView::UpdateColorsFromTheme(const ui::NativeTheme* theme) { - if (!color_explicitly_set_) { - color_ = GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_WindowBackground); - } + if (!color_explicitly_set_) + color_ = theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground); set_background(Background::CreateSolidBackground(color())); BubbleFrameView* frame_view = GetBubbleFrameView(); if (frame_view) @@ -341,9 +297,12 @@ void BubbleDelegateView::UpdateColorsFromTheme(const ui::NativeTheme* theme) { } void BubbleDelegateView::HandleVisibilityChanged(Widget* widget, bool visible) { - if (widget == GetWidget() && visible && anchor_widget() && + if (widget == GetWidget() && anchor_widget() && anchor_widget()->GetTopLevelWidget()) { - anchor_widget()->GetTopLevelWidget()->DisableInactiveRendering(); + if (visible) + anchor_widget()->GetTopLevelWidget()->DisableInactiveRendering(); + else + anchor_widget()->GetTopLevelWidget()->EnableInactiveRendering(); } } diff --git a/chromium/ui/views/bubble/bubble_delegate.h b/chromium/ui/views/bubble/bubble_delegate.h index 4bf8ff68c73..e61111b096d 100644 --- a/chromium/ui/views/bubble/bubble_delegate.h +++ b/chromium/ui/views/bubble/bubble_delegate.h @@ -6,15 +6,14 @@ #define UI_VIEWS_BUBBLE_BUBBLE_DELEGATE_H_ #include "base/gtest_prod_util.h" -#include "ui/gfx/animation/animation_delegate.h" #include "ui/views/bubble/bubble_border.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" #include "ui/views/widget/widget_observer.h" namespace gfx { +class FontList; class Rect; -class SlideAnimation; } namespace views { @@ -24,7 +23,6 @@ class BubbleFrameView; // BubbleDelegateView creates frame and client views for bubble Widgets. // BubbleDelegateView itself is the client's contents view. class VIEWS_EXPORT BubbleDelegateView : public WidgetDelegateView, - public gfx::AnimationDelegate, public WidgetObserver { public: BubbleDelegateView(); @@ -40,7 +38,7 @@ class VIEWS_EXPORT BubbleDelegateView : public WidgetDelegateView, virtual bool ShouldShowCloseButton() const OVERRIDE; virtual View* GetContentsView() OVERRIDE; virtual NonClientFrameView* CreateNonClientFrameView(Widget* widget) OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; // WidgetObserver overrides: virtual void OnWidgetDestroying(Widget* widget) OVERRIDE; @@ -97,23 +95,13 @@ class VIEWS_EXPORT BubbleDelegateView : public WidgetDelegateView, bool adjust_if_offscreen() const { return adjust_if_offscreen_; } void set_adjust_if_offscreen(bool adjust) { adjust_if_offscreen_ = adjust; } - bool move_with_anchor() const { return move_with_anchor_; } - void set_move_with_anchor(bool move) { move_with_anchor_ = move; } - // Get the arrow's anchor rect in screen space. - virtual gfx::Rect GetAnchorRect(); + virtual gfx::Rect GetAnchorRect() const; // Allows delegates to provide custom parameters before widget initialization. virtual void OnBeforeBubbleWidgetInit(Widget::InitParams* params, Widget* widget) const; - // Fade the bubble in or out by animation Widget transparency. - // Fade-in calls Widget::Show; fade-out calls Widget::Close upon completion. - void StartFade(bool fade_in); - - // Restores bubble opacity to its value before StartFade() was called. - void ResetFade(); - // Sets the bubble alignment relative to the anchor. This may only be called // after calling CreateBubble. void SetAlignment(BubbleBorder::BubbleAlignment alignment); @@ -130,17 +118,14 @@ class VIEWS_EXPORT BubbleDelegateView : public WidgetDelegateView, // Get bubble bounds from the anchor rect and client view's preferred size. virtual gfx::Rect GetBubbleBounds(); - // Returns the duration in milliseconds for the fade animation. - virtual int GetFadeDuration(); + // Return a FontList to use for the title of the bubble. + // (The default is MediumFont). + virtual const gfx::FontList& GetTitleFontList() const; // View overrides: virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE; virtual void OnNativeThemeChanged(const ui::NativeTheme* theme) OVERRIDE; - // gfx::AnimationDelegate overrides: - virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE; - virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE; - // Perform view initialization on the contents for bubble sizing. virtual void Init(); @@ -158,6 +143,7 @@ class VIEWS_EXPORT BubbleDelegateView : public WidgetDelegateView, private: friend class BubbleBorderDelegate; + friend class BubbleWindowTargeter; FRIEND_TEST_ALL_PREFIXES(BubbleDelegateTest, CreateDelegate); FRIEND_TEST_ALL_PREFIXES(BubbleDelegateTest, NonClientHitTest); @@ -168,9 +154,6 @@ class VIEWS_EXPORT BubbleDelegateView : public WidgetDelegateView, // Handles widget visibility changes. void HandleVisibilityChanged(Widget* widget, bool visible); - // Fade animation for bubble. - scoped_ptr<gfx::SlideAnimation> fade_animation_; - // Flags controlling bubble closure on the escape key and deactivation. bool close_on_esc_; bool close_on_deactivate_; @@ -182,10 +165,7 @@ class VIEWS_EXPORT BubbleDelegateView : public WidgetDelegateView, Widget* anchor_widget_; // The anchor rect used in the absence of an anchor view. - gfx::Rect anchor_rect_; - - // If true, the bubble will re-anchor (and may resize) with |anchor_widget_|. - bool move_with_anchor_; + mutable gfx::Rect anchor_rect_; // The arrow's location on the bubble. BubbleBorder::Arrow arrow_; @@ -203,12 +183,6 @@ class VIEWS_EXPORT BubbleDelegateView : public WidgetDelegateView, // Insets applied to the |anchor_view_| bounds. gfx::Insets anchor_view_insets_; - // Original opacity of the bubble. - int original_opacity_; - - // The widget hosting the border for this bubble (non-Aura Windows only). - Widget* border_widget_; - // If true, the bubble does not take focus on display; default is false. bool use_focusless_; diff --git a/chromium/ui/views/bubble/bubble_delegate_unittest.cc b/chromium/ui/views/bubble/bubble_delegate_unittest.cc index 7e2c718f950..9070c2fe758 100644 --- a/chromium/ui/views/bubble/bubble_delegate_unittest.cc +++ b/chromium/ui/views/bubble/bubble_delegate_unittest.cc @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/run_loop.h" #include "ui/base/hit_test.h" #include "ui/views/bubble/bubble_delegate.h" #include "ui/views/bubble/bubble_frame_view.h" @@ -11,10 +10,6 @@ #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_observer.h" -#if defined(USE_AURA) -#include "ui/aura/env.h" -#endif - namespace views { namespace { @@ -39,8 +34,9 @@ class TestBubbleDelegateView : public BubbleDelegateView { // BubbleDelegateView overrides: virtual View* GetInitiallyFocusedView() OVERRIDE { return view_; } - virtual gfx::Size GetPreferredSize() OVERRIDE { return gfx::Size(200, 200); } - virtual int GetFadeDuration() OVERRIDE { return 1; } + virtual gfx::Size GetPreferredSize() const OVERRIDE { + return gfx::Size(200, 200); + } private: View* view_; @@ -105,12 +101,10 @@ TEST_F(BubbleDelegateTest, CloseAnchorWidget) { EXPECT_EQ(anchor_widget, bubble_delegate->anchor_widget()); EXPECT_FALSE(bubble_observer.widget_closed()); -#if defined(USE_AURA) // TODO(msw): Remove activation hack to prevent bookkeeping errors in: // aura::test::TestActivationClient::OnWindowDestroyed(). scoped_ptr<Widget> smoke_and_mirrors_widget(CreateTestWidget()); EXPECT_FALSE(bubble_observer.widget_closed()); -#endif // Ensure that closing the anchor widget also closes the bubble itself. anchor_widget->CloseNow(); @@ -198,12 +192,10 @@ TEST_F(BubbleDelegateTest, ResetAnchorWidget) { EXPECT_NE(anchor_widget, bubble_delegate->anchor_widget()); EXPECT_FALSE(bubble_observer.widget_closed()); -#if defined(USE_AURA) // TODO(msw): Remove activation hack to prevent bookkeeping errors in: // aura::test::TestActivationClient::OnWindowDestroyed(). scoped_ptr<Widget> smoke_and_mirrors_widget(CreateTestWidget()); EXPECT_FALSE(bubble_observer.widget_closed()); -#endif // Ensure that closing the parent widget also closes the bubble itself. parent_widget->CloseNow(); @@ -244,7 +236,7 @@ TEST_F(BubbleDelegateTest, NonClientHitTest) { } } -TEST_F(BubbleDelegateTest, CloseWhenAnchorWidgetBoundsChanged) { +TEST_F(BubbleDelegateTest, VisibleWhenAnchorWidgetBoundsChanged) { scoped_ptr<Widget> anchor_widget(CreateTestWidget()); BubbleDelegateView* bubble_delegate = new BubbleDelegateView( anchor_widget->GetContentsView(), BubbleBorder::NONE); @@ -255,66 +247,7 @@ TEST_F(BubbleDelegateTest, CloseWhenAnchorWidgetBoundsChanged) { bubble_widget->Show(); EXPECT_TRUE(bubble_widget->IsVisible()); anchor_widget->SetBounds(gfx::Rect(10, 10, 100, 100)); - EXPECT_FALSE(bubble_widget->IsVisible()); -} - -// This class provides functionality to verify that the BubbleView shows up -// when we call BubbleDelegateView::StartFade(true) and is destroyed when we -// call BubbleDelegateView::StartFade(false). -class BubbleWidgetClosingTest : public BubbleDelegateTest, - public views::WidgetObserver { - public: - BubbleWidgetClosingTest() : bubble_destroyed_(false) { -#if defined(USE_AURA) - aura::Env::CreateInstance(); - loop_.set_dispatcher(aura::Env::GetInstance()->GetDispatcher()); -#endif - } - - virtual ~BubbleWidgetClosingTest() {} - - void Observe(views::Widget* widget) { - widget->AddObserver(this); - } - - // views::WidgetObserver overrides. - virtual void OnWidgetDestroyed(Widget* widget) OVERRIDE { - bubble_destroyed_ = true; - widget->RemoveObserver(this); - loop_.Quit(); - } - - bool bubble_destroyed() const { return bubble_destroyed_; } - - void RunNestedLoop() { - loop_.Run(); - } - - private: - bool bubble_destroyed_; - base::RunLoop loop_; - - DISALLOW_COPY_AND_ASSIGN(BubbleWidgetClosingTest); -}; - -TEST_F(BubbleWidgetClosingTest, TestBubbleVisibilityAndClose) { - scoped_ptr<Widget> anchor_widget(CreateTestWidget()); - TestBubbleDelegateView* bubble_delegate = - new TestBubbleDelegateView(anchor_widget->GetContentsView()); - Widget* bubble_widget = BubbleDelegateView::CreateBubble(bubble_delegate); - EXPECT_FALSE(bubble_widget->IsVisible()); - - bubble_delegate->StartFade(true); EXPECT_TRUE(bubble_widget->IsVisible()); - - EXPECT_EQ(bubble_delegate->GetInitiallyFocusedView(), - bubble_widget->GetFocusManager()->GetFocusedView()); - - Observe(bubble_widget); - - bubble_delegate->StartFade(false); - RunNestedLoop(); - EXPECT_TRUE(bubble_destroyed()); } } // namespace views diff --git a/chromium/ui/views/bubble/bubble_frame_view.cc b/chromium/ui/views/bubble/bubble_frame_view.cc index 7e0fd5e745a..384c4fa46a8 100644 --- a/chromium/ui/views/bubble/bubble_frame_view.cc +++ b/chromium/ui/views/bubble/bubble_frame_view.cc @@ -12,6 +12,7 @@ #include "ui/gfx/path.h" #include "ui/gfx/screen.h" #include "ui/gfx/skia_util.h" +#include "ui/native_theme/native_theme.h" #include "ui/views/bubble/bubble_border.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/widget/widget.h" @@ -20,10 +21,11 @@ namespace { -// Padding, in pixels, for the title view, when it exists. +// Insets for the title bar views in pixels. const int kTitleTopInset = 12; const int kTitleLeftInset = 19; const int kTitleBottomInset = 12; +const int kTitleRightInset = 7; // Get the |vertical| or horizontal amount that |available_bounds| overflows // |window_bounds|. @@ -57,7 +59,8 @@ const char BubbleFrameView::kViewClassName[] = "BubbleFrameView"; // static gfx::Insets BubbleFrameView::GetTitleInsets() { - return gfx::Insets(kTitleTopInset, kTitleLeftInset, kTitleBottomInset, 0); + return gfx::Insets(kTitleTopInset, kTitleLeftInset, + kTitleBottomInset, kTitleRightInset); } BubbleFrameView::BubbleFrameView(const gfx::Insets& content_margins) @@ -67,19 +70,20 @@ BubbleFrameView::BubbleFrameView(const gfx::Insets& content_margins) close_(NULL), titlebar_extra_view_(NULL) { ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - title_ = new Label(string16(), rb.GetFont(ui::ResourceBundle::MediumFont)); + title_ = new Label(base::string16(), + rb.GetFontList(ui::ResourceBundle::MediumFont)); title_->SetHorizontalAlignment(gfx::ALIGN_LEFT); AddChildView(title_); - close_ = new LabelButton(this, string16()); + close_ = new LabelButton(this, base::string16()); close_->SetImage(CustomButton::STATE_NORMAL, *rb.GetImageNamed(IDR_CLOSE_DIALOG).ToImageSkia()); close_->SetImage(CustomButton::STATE_HOVERED, *rb.GetImageNamed(IDR_CLOSE_DIALOG_H).ToImageSkia()); close_->SetImage(CustomButton::STATE_PRESSED, *rb.GetImageNamed(IDR_CLOSE_DIALOG_P).ToImageSkia()); + close_->SetBorder(scoped_ptr<Border>()); close_->SetSize(close_->GetPreferredSize()); - close_->set_border(NULL); close_->SetVisible(false); AddChildView(close_); } @@ -154,11 +158,15 @@ void BubbleFrameView::UpdateWindowIcon() {} void BubbleFrameView::UpdateWindowTitle() { title_->SetText(GetWidget()->widget_delegate()->ShouldShowWindowTitle() ? - GetWidget()->widget_delegate()->GetWindowTitle() : string16()); + GetWidget()->widget_delegate()->GetWindowTitle() : base::string16()); // Update the close button visibility too, otherwise it's not intialized. ResetWindowControls(); } +void BubbleFrameView::SetTitleFontList(const gfx::FontList& font_list) { + title_->SetFontList(font_list); +} + gfx::Insets BubbleFrameView::GetInsets() const { gfx::Insets insets = content_margins_; const int title_height = title_->text().empty() ? 0 : @@ -168,39 +176,40 @@ gfx::Insets BubbleFrameView::GetInsets() const { return insets; } -gfx::Size BubbleFrameView::GetPreferredSize() { +gfx::Size BubbleFrameView::GetPreferredSize() const { return GetSizeForClientSize(GetWidget()->client_view()->GetPreferredSize()); } -gfx::Size BubbleFrameView::GetMinimumSize() { +gfx::Size BubbleFrameView::GetMinimumSize() const { return GetSizeForClientSize(GetWidget()->client_view()->GetMinimumSize()); } void BubbleFrameView::Layout() { - gfx::Rect bounds(GetLocalBounds()); - bounds.Inset(border()->GetInsets()); - // Small additional insets yield the desired 10px visual close button insets. - bounds.Inset(0, 0, close_->width() + 1, 0); - close_->SetPosition(gfx::Point(bounds.right(), bounds.y() + 2)); - - gfx::Rect title_bounds(bounds); - title_bounds.Inset(kTitleLeftInset, kTitleTopInset, 0, 0); + gfx::Rect bounds(GetContentsBounds()); + bounds.Inset(GetTitleInsets()); + if (bounds.IsEmpty()) + return; + + // The close button top inset is actually smaller than the title top inset. + close_->SetPosition(gfx::Point(bounds.right() - close_->width(), + bounds.y() - 5)); + gfx::Size title_size(title_->GetPreferredSize()); - const int title_width = std::max(0, close_->bounds().x() - title_bounds.x()); + const int title_width = std::max(0, close_->x() - bounds.x()); title_size.SetToMin(gfx::Size(title_width, title_size.height())); - title_bounds.set_size(title_size); - title_->SetBoundsRect(title_bounds); + bounds.set_size(title_size); + title_->SetBoundsRect(bounds); if (titlebar_extra_view_) { - const int extra_width = close_->bounds().x() - title_->bounds().right(); + const int extra_width = close_->x() - title_->bounds().right(); gfx::Size size = titlebar_extra_view_->GetPreferredSize(); size.SetToMin(gfx::Size(std::max(0, extra_width), size.height())); gfx::Rect titlebar_extra_view_bounds( - bounds.right() - size.width(), - title_bounds.y(), + close_->x() - size.width(), + bounds.y(), size.width(), - title_bounds.height()); - titlebar_extra_view_bounds.Subtract(title_bounds); + bounds.height()); + titlebar_extra_view_bounds.Subtract(bounds); titlebar_extra_view_->SetBoundsRect(titlebar_extra_view_bounds); } } @@ -220,17 +229,25 @@ void BubbleFrameView::OnThemeChanged() { UpdateWindowIcon(); } +void BubbleFrameView::OnNativeThemeChanged(const ui::NativeTheme* theme) { + if (bubble_border_ && bubble_border_->use_theme_background_color()) { + bubble_border_->set_background_color(GetNativeTheme()-> + GetSystemColor(ui::NativeTheme::kColorId_DialogBackground)); + SchedulePaint(); + } +} + void BubbleFrameView::ButtonPressed(Button* sender, const ui::Event& event) { if (sender == close_) GetWidget()->Close(); } -void BubbleFrameView::SetBubbleBorder(BubbleBorder* border) { - bubble_border_ = border; - set_border(bubble_border_); +void BubbleFrameView::SetBubbleBorder(scoped_ptr<BubbleBorder> border) { + bubble_border_ = border.get(); + SetBorder(border.PassAs<Border>()); // Update the background, which relies on the border. - set_background(new views::BubbleBackground(border)); + set_background(new views::BubbleBackground(bubble_border_)); } void BubbleFrameView::SetTitlebarExtraView(View* view) { @@ -243,27 +260,23 @@ void BubbleFrameView::SetTitlebarExtraView(View* view) { gfx::Rect BubbleFrameView::GetUpdatedWindowBounds(const gfx::Rect& anchor_rect, gfx::Size client_size, bool adjust_if_offscreen) { - gfx::Insets insets(GetInsets()); - client_size.Enlarge(insets.width(), insets.height()); + gfx::Size size(GetSizeForClientSize(client_size)); const BubbleBorder::Arrow arrow = bubble_border_->arrow(); if (adjust_if_offscreen && BubbleBorder::has_arrow(arrow)) { + // Try to mirror the anchoring if the bubble does not fit on the screen. if (!bubble_border_->is_arrow_at_center(arrow)) { - // Try to mirror the anchoring if the bubble does not fit on the screen. - MirrorArrowIfOffScreen(true, anchor_rect, client_size); - MirrorArrowIfOffScreen(false, anchor_rect, client_size); + MirrorArrowIfOffScreen(true, anchor_rect, size); + MirrorArrowIfOffScreen(false, anchor_rect, size); } else { - // Mirror as needed vertically if the arrow is on a horizontal edge and - // vice-versa. - MirrorArrowIfOffScreen(BubbleBorder::is_arrow_on_horizontal(arrow), - anchor_rect, - client_size); - OffsetArrowIfOffScreen(anchor_rect, client_size); + const bool mirror_vertical = BubbleBorder::is_arrow_on_horizontal(arrow); + MirrorArrowIfOffScreen(mirror_vertical, anchor_rect, size); + OffsetArrowIfOffScreen(anchor_rect, size); } } // Calculate the bounds with the arrow in its updated location and offset. - return bubble_border_->GetBounds(anchor_rect, client_size); + return bubble_border_->GetBounds(anchor_rect, size); } gfx::Rect BubbleFrameView::GetAvailableScreenBounds(const gfx::Rect& rect) { @@ -337,9 +350,8 @@ void BubbleFrameView::OffsetArrowIfOffScreen(const gfx::Rect& anchor_rect, SchedulePaint(); } -gfx::Size BubbleFrameView::GetSizeForClientSize(const gfx::Size& client_size) { - gfx::Size size( - GetUpdatedWindowBounds(gfx::Rect(), client_size, false).size()); +gfx::Size BubbleFrameView::GetSizeForClientSize( + const gfx::Size& client_size) const { // Accommodate the width of the title bar elements. int title_bar_width = GetInsets().width() + border()->GetInsets().width(); if (!title_->text().empty()) @@ -348,7 +360,10 @@ gfx::Size BubbleFrameView::GetSizeForClientSize(const gfx::Size& client_size) { title_bar_width += close_->width() + 1; if (titlebar_extra_view_ != NULL) title_bar_width += titlebar_extra_view_->GetPreferredSize().width(); + gfx::Size size(client_size); size.SetToMax(gfx::Size(title_bar_width, 0)); + const gfx::Insets insets(GetInsets()); + size.Enlarge(insets.width(), insets.height()); return size; } diff --git a/chromium/ui/views/bubble/bubble_frame_view.h b/chromium/ui/views/bubble/bubble_frame_view.h index d033c8d0d10..a17a4f8f487 100644 --- a/chromium/ui/views/bubble/bubble_frame_view.h +++ b/chromium/ui/views/bubble/bubble_frame_view.h @@ -12,6 +12,10 @@ #include "ui/views/controls/button/button.h" #include "ui/views/window/non_client_view.h" +namespace gfx { +class FontList; +} + namespace views { class Label; @@ -43,21 +47,26 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, virtual void UpdateWindowIcon() OVERRIDE; virtual void UpdateWindowTitle() OVERRIDE; + // Set the FontList to be used for the title of the bubble. + // Caller must arrange to update the layout to have the call take effect. + void SetTitleFontList(const gfx::FontList& font_list); + // View overrides: virtual gfx::Insets GetInsets() const OVERRIDE; - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual gfx::Size GetMinimumSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual gfx::Size GetMinimumSize() const OVERRIDE; virtual void Layout() OVERRIDE; virtual const char* GetClassName() const OVERRIDE; virtual void ChildPreferredSizeChanged(View* child) OVERRIDE; virtual void OnThemeChanged() OVERRIDE; + virtual void OnNativeThemeChanged(const ui::NativeTheme* theme) OVERRIDE; // Overridden from ButtonListener: virtual void ButtonPressed(Button* sender, const ui::Event& event) OVERRIDE; - // Use bubble_border() and SetBubbleBorder(), not border() and set_border(). + // Use bubble_border() and SetBubbleBorder(), not border() and SetBorder(). BubbleBorder* bubble_border() const { return bubble_border_; } - void SetBubbleBorder(BubbleBorder* border); + void SetBubbleBorder(scoped_ptr<BubbleBorder> border); gfx::Insets content_margins() const { return content_margins_; } @@ -89,7 +98,7 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, const gfx::Size& client_size); // Calculates the size needed to accommodate the given client area. - gfx::Size GetSizeForClientSize(const gfx::Size& client_size); + gfx::Size GetSizeForClientSize(const gfx::Size& client_size) const; // The bubble border. BubbleBorder* bubble_border_; diff --git a/chromium/ui/views/bubble/bubble_frame_view_unittest.cc b/chromium/ui/views/bubble/bubble_frame_view_unittest.cc index ef5d58d8b00..1bd9e348a18 100644 --- a/chromium/ui/views/bubble/bubble_frame_view_unittest.cc +++ b/chromium/ui/views/bubble/bubble_frame_view_unittest.cc @@ -23,7 +23,8 @@ class TestBubbleFrameView : public BubbleFrameView { TestBubbleFrameView() : BubbleFrameView(gfx::Insets(kMargin, kMargin, kMargin, kMargin)), available_bounds_(gfx::Rect(0, 0, 1000, 1000)) { - SetBubbleBorder(new BubbleBorder(kArrow, BubbleBorder::NO_SHADOW, kColor)); + SetBubbleBorder(scoped_ptr<views::BubbleBorder>( + new BubbleBorder(kArrow, BubbleBorder::NO_SHADOW, kColor))); } virtual ~TestBubbleFrameView() {} diff --git a/chromium/ui/views/bubble/bubble_window_targeter.cc b/chromium/ui/views/bubble/bubble_window_targeter.cc new file mode 100644 index 00000000000..19e0c85f91a --- /dev/null +++ b/chromium/ui/views/bubble/bubble_window_targeter.cc @@ -0,0 +1,30 @@ +// Copyright 2014 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/bubble/bubble_window_targeter.h" + +#include "ui/aura/window.h" +#include "ui/gfx/path.h" +#include "ui/gfx/skia_util.h" +#include "ui/views/bubble/bubble_delegate.h" +#include "ui/views/bubble/bubble_frame_view.h" + +namespace views { + +BubbleWindowTargeter::BubbleWindowTargeter(BubbleDelegateView* bubble) + : wm::MaskedWindowTargeter(bubble->GetWidget()->GetNativeView()), + bubble_(bubble) { +} + +BubbleWindowTargeter::~BubbleWindowTargeter() { +} + +bool BubbleWindowTargeter::GetHitTestMask(aura::Window* window, + gfx::Path* mask) const { + mask->addRect( + gfx::RectToSkRect(bubble_->GetBubbleFrameView()->GetContentsBounds())); + return true; +} + +} // namespace views diff --git a/chromium/ui/views/bubble/bubble_window_targeter.h b/chromium/ui/views/bubble/bubble_window_targeter.h new file mode 100644 index 00000000000..45b1d5c551b --- /dev/null +++ b/chromium/ui/views/bubble/bubble_window_targeter.h @@ -0,0 +1,33 @@ +// Copyright 2014 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/views_export.h" +#include "ui/wm/core/masked_window_targeter.h" + +namespace aura { +class Window; +} + +namespace views { +class BubbleDelegateView; + +// A convenient window-targeter that uses a mask based on the content-bounds of +// the bubble-frame. +class VIEWS_EXPORT BubbleWindowTargeter + : public NON_EXPORTED_BASE(wm::MaskedWindowTargeter) { + public: + explicit BubbleWindowTargeter(BubbleDelegateView* bubble); + virtual ~BubbleWindowTargeter(); + + private: + // wm::MaskedWindowTargeter: + virtual bool GetHitTestMask(aura::Window* window, + gfx::Path* mask) const OVERRIDE; + + views::BubbleDelegateView* bubble_; + + DISALLOW_COPY_AND_ASSIGN(BubbleWindowTargeter); +}; + +} // namespace views diff --git a/chromium/ui/views/bubble/bubble_window_targeter_unittest.cc b/chromium/ui/views/bubble/bubble_window_targeter_unittest.cc new file mode 100644 index 00000000000..479f6e93d39 --- /dev/null +++ b/chromium/ui/views/bubble/bubble_window_targeter_unittest.cc @@ -0,0 +1,116 @@ +// Copyright 2014 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/bubble/bubble_window_targeter.h" + +#include "ui/aura/window.h" +#include "ui/aura/window_event_dispatcher.h" +#include "ui/views/bubble/bubble_border.h" +#include "ui/views/bubble/bubble_delegate.h" +#include "ui/views/test/views_test_base.h" +#include "ui/views/widget/widget.h" + +namespace views { + +namespace { + +class WidgetOwnsNativeBubble : public BubbleDelegateView { + public: + WidgetOwnsNativeBubble(View* content, BubbleBorder::Arrow arrow) + : BubbleDelegateView(content, arrow) { + } + + virtual ~WidgetOwnsNativeBubble() {} + + private: + // BubbleDelegateView: + virtual void OnBeforeBubbleWidgetInit(Widget::InitParams* params, + Widget* widget) const OVERRIDE { + params->ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + } + + DISALLOW_COPY_AND_ASSIGN(WidgetOwnsNativeBubble); +}; + +} // namespace + +class BubbleWindowTargeterTest : public ViewsTestBase { + public: + BubbleWindowTargeterTest() + : bubble_delegate_(NULL) { + } + virtual ~BubbleWindowTargeterTest() {} + + virtual void SetUp() OVERRIDE { + ViewsTestBase::SetUp(); + CreateAnchorWidget(); + CreateBubbleWidget(); + + anchor_widget()->Show(); + bubble_widget()->Show(); + } + + virtual void TearDown() OVERRIDE { + bubble_delegate_ = NULL; + bubble_widget_.reset(); + anchor_.reset(); + ViewsTestBase::TearDown(); + } + + Widget* anchor_widget() { return anchor_.get(); } + Widget* bubble_widget() { return bubble_widget_.get(); } + BubbleDelegateView* bubble_delegate() { return bubble_delegate_; } + + private: + void CreateAnchorWidget() { + anchor_.reset(new Widget()); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + anchor_->Init(params); + } + + void CreateBubbleWidget() { + bubble_delegate_ = new WidgetOwnsNativeBubble( + anchor_->GetContentsView(), BubbleBorder::NONE); + bubble_delegate_->set_color(SK_ColorGREEN); + bubble_widget_.reset(BubbleDelegateView::CreateBubble(bubble_delegate_)); + } + + scoped_ptr<Widget> anchor_; + scoped_ptr<Widget> bubble_widget_; + BubbleDelegateView* bubble_delegate_; + + DISALLOW_COPY_AND_ASSIGN(BubbleWindowTargeterTest); +}; + +TEST_F(BubbleWindowTargeterTest, HitTest) { + ui::EventTarget* root = bubble_widget()->GetNativeWindow()->GetRootWindow(); + ui::EventTargeter* targeter = root->GetEventTargeter(); + aura::Window* bubble_window = bubble_widget()->GetNativeWindow(); + gfx::Rect bubble_bounds = bubble_window->GetBoundsInRootWindow(); + + { + bubble_delegate()->set_margins(gfx::Insets()); + ui::MouseEvent move1(ui::ET_MOUSE_MOVED, bubble_bounds.origin(), + bubble_bounds.origin(), ui::EF_NONE, ui::EF_NONE); + EXPECT_EQ(bubble_window, targeter->FindTargetForEvent(root, &move1)); + } + { + bubble_delegate()->set_margins(gfx::Insets(20, 20, 20, 20)); + ui::MouseEvent move1(ui::ET_MOUSE_MOVED, bubble_bounds.origin(), + bubble_bounds.origin(), ui::EF_NONE, ui::EF_NONE); + EXPECT_EQ(bubble_window, targeter->FindTargetForEvent(root, &move1)); + } + + bubble_window->SetEventTargeter(scoped_ptr<ui::EventTargeter>( + new BubbleWindowTargeter(bubble_delegate()))); + { + bubble_delegate()->set_margins(gfx::Insets(20, 20, 20, 20)); + ui::MouseEvent move1(ui::ET_MOUSE_MOVED, bubble_bounds.origin(), + bubble_bounds.origin(), ui::EF_NONE, ui::EF_NONE); + EXPECT_NE(bubble_window, targeter->FindTargetForEvent(root, &move1)); + } +} + +} // namespace views diff --git a/chromium/ui/views/bubble/tray_bubble_view.cc b/chromium/ui/views/bubble/tray_bubble_view.cc index 3f947ab9134..243e58b3cfe 100644 --- a/chromium/ui/views/bubble/tray_bubble_view.cc +++ b/chromium/ui/views/bubble/tray_bubble_view.cc @@ -11,7 +11,8 @@ #include "third_party/skia/include/core/SkPaint.h" #include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/effects/SkBlurImageFilter.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" +#include "ui/aura/window.h" #include "ui/base/l10n/l10n_util.h" #include "ui/compositor/layer.h" #include "ui/compositor/layer_delegate.h" @@ -22,6 +23,7 @@ #include "ui/gfx/rect.h" #include "ui/gfx/skia_util.h" #include "ui/views/bubble/bubble_frame_view.h" +#include "ui/views/bubble/bubble_window_targeter.h" #include "ui/views/layout/box_layout.h" #include "ui/views/widget/widget.h" @@ -325,17 +327,14 @@ TrayBubbleView::TrayBubbleView(gfx::NativeView parent_window, mouse_actively_entered_(false) { set_parent_window(parent_window); set_notify_enter_exit_on_child(true); - set_move_with_anchor(true); set_close_on_deactivate(init_params.close_on_deactivate); set_margins(gfx::Insets()); bubble_border_ = new TrayBubbleBorder(this, GetAnchorView(), params_); - if (get_use_acceleration_when_possible()) { - SetPaintToLayer(true); - SetFillsBoundsOpaquely(true); + SetPaintToLayer(true); + SetFillsBoundsOpaquely(true); - bubble_content_mask_.reset( - new TrayBubbleContentMask(bubble_border_->GetBorderCornerRadius())); - } + bubble_content_mask_.reset( + new TrayBubbleContentMask(bubble_border_->GetBorderCornerRadius())); } TrayBubbleView::~TrayBubbleView() { @@ -350,17 +349,17 @@ void TrayBubbleView::InitializeAndShowBubble() { SetAlignment(params_.arrow_alignment); bubble_border_->UpdateArrowOffset(); - if (get_use_acceleration_when_possible()) - layer()->parent()->SetMaskLayer(bubble_content_mask_->layer()); + layer()->parent()->SetMaskLayer(bubble_content_mask_->layer()); GetWidget()->Show(); + GetWidget()->GetNativeWindow()->SetEventTargeter( + scoped_ptr<ui::EventTargeter>(new BubbleWindowTargeter(this))); UpdateBubble(); } void TrayBubbleView::UpdateBubble() { SizeToContents(); - if (get_use_acceleration_when_possible()) - bubble_content_mask_->layer()->SetBounds(layer()->bounds()); + bubble_content_mask_->layer()->SetBounds(layer()->bounds()); GetWidget()->GetRootView()->SchedulePaint(); } @@ -390,11 +389,11 @@ gfx::Insets TrayBubbleView::GetBorderInsets() const { void TrayBubbleView::Init() { BoxLayout* layout = new BottomAlignedBoxLayout(this); - layout->set_spread_blank_space(true); + layout->set_main_axis_alignment(views::BoxLayout::MAIN_AXIS_ALIGNMENT_FILL); SetLayoutManager(layout); } -gfx::Rect TrayBubbleView::GetAnchorRect() { +gfx::Rect TrayBubbleView::GetAnchorRect() const { if (!delegate_) return gfx::Rect(); return delegate_->GetAnchorRect(anchor_widget(), @@ -408,7 +407,7 @@ bool TrayBubbleView::CanActivate() const { NonClientFrameView* TrayBubbleView::CreateNonClientFrameView(Widget* widget) { BubbleFrameView* frame = new BubbleFrameView(margins()); - frame->SetBubbleBorder(bubble_border_); + frame->SetBubbleBorder(scoped_ptr<views::BubbleBorder>(bubble_border_)); return frame; } @@ -421,21 +420,21 @@ void TrayBubbleView::GetWidgetHitTestMask(gfx::Path* mask) const { mask->addRect(gfx::RectToSkRect(GetBubbleFrameView()->GetContentsBounds())); } -gfx::Size TrayBubbleView::GetPreferredSize() { +gfx::Size TrayBubbleView::GetPreferredSize() const { return gfx::Size(preferred_width_, GetHeightForWidth(preferred_width_)); } -gfx::Size TrayBubbleView::GetMaximumSize() { +gfx::Size TrayBubbleView::GetMaximumSize() const { gfx::Size size = GetPreferredSize(); size.set_width(params_.max_width); return size; } -int TrayBubbleView::GetHeightForWidth(int width) { +int TrayBubbleView::GetHeightForWidth(int width) const { int height = GetInsets().height(); width = std::max(width - GetInsets().width(), 0); for (int i = 0; i < child_count(); ++i) { - View* child = child_at(i); + const View* child = child_at(i); if (child->visible()) height += child->GetHeightForWidth(width); } @@ -478,9 +477,9 @@ void TrayBubbleView::OnMouseExited(const ui::MouseEvent& event) { delegate_->OnMouseExitedView(); } -void TrayBubbleView::GetAccessibleState(ui::AccessibleViewState* state) { +void TrayBubbleView::GetAccessibleState(ui::AXViewState* state) { if (delegate_ && params_.can_activate) { - state->role = ui::AccessibilityTypes::ROLE_WINDOW; + state->role = ui::AX_ROLE_WINDOW; state->name = delegate_->GetAccessibleNameForBubble(); } } @@ -500,8 +499,7 @@ void TrayBubbleView::ChildPreferredSizeChanged(View* child) { void TrayBubbleView::ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) { - if (get_use_acceleration_when_possible() && details.is_add && - details.child == this) { + if (details.is_add && details.child == this) { details.parent->SetPaintToLayer(true); details.parent->SetFillsBoundsOpaquely(true); details.parent->layer()->SetMasksToBounds(true); diff --git a/chromium/ui/views/bubble/tray_bubble_view.h b/chromium/ui/views/bubble/tray_bubble_view.h index 1e9d48298ea..54f6cc88033 100644 --- a/chromium/ui/views/bubble/tray_bubble_view.h +++ b/chromium/ui/views/bubble/tray_bubble_view.h @@ -71,13 +71,14 @@ class VIEWS_EXPORT TrayBubbleView : public views::BubbleDelegateView, // Called from GetAccessibleState(); should return the appropriate // accessible name for the bubble. - virtual string16 GetAccessibleNameForBubble() = 0; + virtual base::string16 GetAccessibleNameForBubble() = 0; // Passes responsibility for BubbleDelegateView::GetAnchorRect to the // delegate. - virtual gfx::Rect GetAnchorRect(views::Widget* anchor_widget, - AnchorType anchor_type, - AnchorAlignment anchor_alignment) = 0; + virtual gfx::Rect GetAnchorRect( + views::Widget* anchor_widget, + AnchorType anchor_type, + AnchorAlignment anchor_alignment) const = 0; // Called when a bubble wants to hide/destroy itself (e.g. last visible // child view was closed). @@ -153,15 +154,15 @@ class VIEWS_EXPORT TrayBubbleView : public views::BubbleDelegateView, virtual void GetWidgetHitTestMask(gfx::Path* mask) const OVERRIDE; // Overridden from views::BubbleDelegateView. - virtual gfx::Rect GetAnchorRect() OVERRIDE; + virtual gfx::Rect GetAnchorRect() const OVERRIDE; // Overridden from views::View. - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual gfx::Size GetMaximumSize() OVERRIDE; - virtual int GetHeightForWidth(int width) OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual gfx::Size GetMaximumSize() const OVERRIDE; + virtual int GetHeightForWidth(int width) const OVERRIDE; virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE; virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; // Overridden from MouseWatcherListener virtual void MouseMovedOutOfHost() OVERRIDE; diff --git a/chromium/ui/views/button_drag_utils.cc b/chromium/ui/views/button_drag_utils.cc index 9305a07d3b0..2c4f25ade3b 100644 --- a/chromium/ui/views/button_drag_utils.cc +++ b/chromium/ui/views/button_drag_utils.cc @@ -10,6 +10,7 @@ #include "ui/base/dragdrop/os_exchange_data.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" +#include "ui/gfx/geometry/point.h" #include "ui/gfx/image/image.h" #include "ui/views/controls/button/text_button.h" #include "ui/views/drag_utils.h" @@ -18,20 +19,29 @@ namespace button_drag_utils { // Maximum width of the link drag image in pixels. -static const int kLinkDragImageMaxWidth = 200; +static const int kLinkDragImageMaxWidth = 150; void SetURLAndDragImage(const GURL& url, - const string16& title, + const base::string16& title, const gfx::ImageSkia& icon, + const gfx::Point* press_pt, ui::OSExchangeData* data, views::Widget* widget) { DCHECK(url.is_valid() && data); - data->SetURL(url, title); + SetDragImage(url, title, icon, press_pt, data, widget); +} +void SetDragImage(const GURL& url, + const base::string16& title, + const gfx::ImageSkia& icon, + const gfx::Point* press_pt, + ui::OSExchangeData* data, + views::Widget* widget) { // Create a button to render the drag image for us. views::TextButton button(NULL, - title.empty() ? UTF8ToUTF16(url.spec()) : title); + title.empty() ? base::UTF8ToUTF16(url.spec()) + : title); button.set_max_width(kLinkDragImageMaxWidth); if (icon.isNull()) { button.SetIcon(*ui::ResourceBundle::GetSharedInstance().GetImageNamed( @@ -42,12 +52,17 @@ void SetURLAndDragImage(const GURL& url, gfx::Size prefsize = button.GetPreferredSize(); button.SetBounds(0, 0, prefsize.width(), prefsize.height()); + gfx::Vector2d press_point; + if (press_pt) + press_point = press_pt->OffsetFromOrigin(); + else + press_point = gfx::Vector2d(prefsize.width() / 2, prefsize.height() / 2); + // Render the image. scoped_ptr<gfx::Canvas> canvas( views::GetCanvasForDragImage(widget, prefsize)); button.PaintButton(canvas.get(), views::TextButton::PB_FOR_DRAG); - drag_utils::SetDragImageOnDataObject(*canvas, prefsize, - gfx::Vector2d(prefsize.width() / 2, prefsize.height() / 2), data); + drag_utils::SetDragImageOnDataObject(*canvas, press_point, data); } } // namespace button_drag_utils diff --git a/chromium/ui/views/button_drag_utils.h b/chromium/ui/views/button_drag_utils.h index 1f93c9517de..8f1a7eedf7d 100644 --- a/chromium/ui/views/button_drag_utils.h +++ b/chromium/ui/views/button_drag_utils.h @@ -12,6 +12,7 @@ class GURL; namespace gfx { class ImageSkia; +class Point; } namespace ui { @@ -25,13 +26,23 @@ class Widget; namespace button_drag_utils { // Sets url and title on data as well as setting a suitable image for dragging. -// The image looks like that of the bookmark buttons. +// The image looks like that of the bookmark buttons. |press_pt| is optional +// offset; otherwise, it centers the drag image. VIEWS_EXPORT void SetURLAndDragImage(const GURL& url, - const string16& title, + const base::string16& title, const gfx::ImageSkia& icon, + const gfx::Point* press_pt, ui::OSExchangeData* data, views::Widget* widget); +// As above, but only sets the image. +VIEWS_EXPORT void SetDragImage(const GURL& url, + const base::string16& title, + const gfx::ImageSkia& icon, + const gfx::Point* press_pt, + ui::OSExchangeData* data, + views::Widget* widget); + } // namespace drag_utils #endif // UI_VIEWS_BUTTON_DRAG_UTILS_H_ diff --git a/chromium/ui/views/cocoa/bridged_content_view.h b/chromium/ui/views/cocoa/bridged_content_view.h new file mode 100644 index 00000000000..e7c83f74af5 --- /dev/null +++ b/chromium/ui/views/cocoa/bridged_content_view.h @@ -0,0 +1,33 @@ +// Copyright 2014 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_COCOA_BRIDGED_CONTENT_VIEW_H_ +#define UI_VIEWS_COCOA_BRIDGED_CONTENT_VIEW_H_ + +#import <Cocoa/Cocoa.h> + +namespace views { +class View; +} + +// The NSView that sits as the root contentView of the NSWindow, whilst it has +// a views::RootView present. Bridges requests from Cocoa to the hosted +// views::View. +@interface BridgedContentView : NSView { + @private + // Weak. The hosted RootView, owned by hostedView_->GetWidget(). + views::View* hostedView_; +} + +@property(readonly, nonatomic) views::View* hostedView; + +// Initialize the NSView -> views::View bridge. |viewToHost| must be non-NULL. +- (id)initWithView:(views::View*)viewToHost; + +// Clear the hosted view. For example, if it is about to be destroyed. +- (void)clearView; + +@end + +#endif // UI_VIEWS_COCOA_BRIDGED_CONTENT_VIEW_H_ diff --git a/chromium/ui/views/cocoa/bridged_content_view.mm b/chromium/ui/views/cocoa/bridged_content_view.mm new file mode 100644 index 00000000000..bd9a511a244 --- /dev/null +++ b/chromium/ui/views/cocoa/bridged_content_view.mm @@ -0,0 +1,50 @@ +// Copyright 2014 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. + +#import "ui/views/cocoa/bridged_content_view.h" + +#include "base/logging.h" +#include "ui/gfx/canvas_paint_mac.h" +#include "ui/views/view.h" + +@implementation BridgedContentView + +@synthesize hostedView = hostedView_; + +- (id)initWithView:(views::View*)viewToHost { + DCHECK(viewToHost); + gfx::Rect bounds = viewToHost->bounds(); + // To keep things simple, assume the origin is (0, 0) until there exists a use + // case for something other than that. + DCHECK(bounds.origin().IsOrigin()); + NSRect initialFrame = NSMakeRect(0, 0, bounds.width(), bounds.height()); + if ((self = [super initWithFrame:initialFrame])) + hostedView_ = viewToHost; + + return self; +} + +- (void)clearView { + hostedView_ = NULL; +} + +// NSView implementation. + +- (void)setFrameSize:(NSSize)newSize { + [super setFrameSize:newSize]; + if (!hostedView_) + return; + + hostedView_->SetSize(gfx::Size(newSize.width, newSize.height)); +} + +- (void)drawRect:(NSRect)dirtyRect { + if (!hostedView_) + return; + + gfx::CanvasSkiaPaint canvas(dirtyRect, false /* opaque */); + hostedView_->Paint(&canvas, views::CullSet()); +} + +@end diff --git a/chromium/ui/views/cocoa/bridged_native_widget.h b/chromium/ui/views/cocoa/bridged_native_widget.h new file mode 100644 index 00000000000..59754d1d129 --- /dev/null +++ b/chromium/ui/views/cocoa/bridged_native_widget.h @@ -0,0 +1,62 @@ +// Copyright 2014 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_COCOA_BRIDGED_NATIVE_WIDGET_H_ +#define UI_VIEWS_COCOA_BRIDGED_NATIVE_WIDGET_H_ + +#import <Cocoa/Cocoa.h> + +#import "base/mac/scoped_nsobject.h" +#include "base/memory/scoped_ptr.h" +#include "ui/views/ime/input_method_delegate.h" +#include "ui/views/views_export.h" + +@class BridgedContentView; + +namespace ui { +class InputMethod; +} + +namespace views { + +class InputMethod; +class View; + +// A bridge to an NSWindow managed by an instance of NativeWidgetMac or +// DesktopNativeWidgetMac. Serves as a helper class to bridge requests from the +// NativeWidgetMac to the Cocoa window. Behaves a bit like an aura::Window. +class VIEWS_EXPORT BridgedNativeWidget : public internal::InputMethodDelegate { + + public: + BridgedNativeWidget(); + virtual ~BridgedNativeWidget(); + + // Initialize the bridge, "retains" ownership of |window|. + void Init(base::scoped_nsobject<NSWindow> window); + + // Set or clears the views::View bridged by the content view. This does NOT + // take ownership of |view|. + void SetRootView(views::View* view); + + // See widget.h for documentation. + InputMethod* CreateInputMethod(); + ui::InputMethod* GetHostInputMethod(); + + BridgedContentView* ns_view() { return bridged_view_; } + NSWindow* ns_window() { return window_; } + + // Overridden from internal::InputMethodDelegate: + virtual void DispatchKeyEventPostIME(const ui::KeyEvent& key) OVERRIDE; + + private: + base::scoped_nsobject<NSWindow> window_; + base::scoped_nsobject<BridgedContentView> bridged_view_; + scoped_ptr<ui::InputMethod> input_method_; + + DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidget); +}; + +} // namespace views + +#endif // UI_VIEWS_COCOA_BRIDGED_NATIVE_WIDGET_H_ diff --git a/chromium/ui/views/cocoa/bridged_native_widget.mm b/chromium/ui/views/cocoa/bridged_native_widget.mm new file mode 100644 index 00000000000..e4b754f78d8 --- /dev/null +++ b/chromium/ui/views/cocoa/bridged_native_widget.mm @@ -0,0 +1,78 @@ +// Copyright 2014 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. + +#import "ui/views/cocoa/bridged_native_widget.h" + +#include "base/logging.h" +#include "ui/base/ime/input_method.h" +#include "ui/base/ime/input_method_factory.h" +#include "ui/base/ui_base_switches_util.h" +#import "ui/views/cocoa/bridged_content_view.h" +#include "ui/views/ime/input_method_bridge.h" +#include "ui/views/ime/null_input_method.h" +#include "ui/views/view.h" +#include "ui/views/widget/widget.h" + +namespace views { + +BridgedNativeWidget::BridgedNativeWidget() { +} + +BridgedNativeWidget::~BridgedNativeWidget() { + SetRootView(NULL); +} + +void BridgedNativeWidget::Init(base::scoped_nsobject<NSWindow> window) { + DCHECK(!window_); + window_.swap(window); +} + +void BridgedNativeWidget::SetRootView(views::View* view) { + if (view == [bridged_view_ hostedView]) + return; + + [bridged_view_ clearView]; + bridged_view_.reset(); + // Note that there can still be references to the old |bridged_view_| + // floating around in Cocoa libraries at this point. However, references to + // the old views::View will be gone, so any method calls will become no-ops. + + if (view) { + bridged_view_.reset([[BridgedContentView alloc] initWithView:view]); + // Objective C initializers can return nil. However, if |view| is non-NULL + // this should be treated as an error and caught early. + CHECK(bridged_view_); + } + [window_ setContentView:bridged_view_]; +} + +InputMethod* BridgedNativeWidget::CreateInputMethod() { + if (switches::IsTextInputFocusManagerEnabled()) + return new NullInputMethod(); + + return new InputMethodBridge(this, GetHostInputMethod(), true); +} + +ui::InputMethod* BridgedNativeWidget::GetHostInputMethod() { + if (!input_method_) { + // Delegate is NULL because Mac IME does not need DispatchKeyEventPostIME + // callbacks. + input_method_ = ui::CreateInputMethod(NULL, nil); + } + return input_method_.get(); +} + +//////////////////////////////////////////////////////////////////////////////// +// BridgedNativeWidget, internal::InputMethodDelegate: + +void BridgedNativeWidget::DispatchKeyEventPostIME(const ui::KeyEvent& key) { + // Mac key events don't go through this, but some unit tests that use + // MockInputMethod do. + Widget* widget = [bridged_view_ hostedView]->GetWidget(); + widget->OnKeyEvent(const_cast<ui::KeyEvent*>(&key)); + if (!key.handled() && widget->GetFocusManager()) + widget->GetFocusManager()->OnKeyEvent(key); +} + +} // namespace views diff --git a/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm b/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm new file mode 100644 index 00000000000..6d30f41f13c --- /dev/null +++ b/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm @@ -0,0 +1,117 @@ +// Copyright 2014 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. + +#import "ui/views/cocoa/bridged_native_widget.h" + +#import <Cocoa/Cocoa.h> + +#include "base/memory/scoped_ptr.h" +#import "testing/gtest_mac.h" +#import "ui/gfx/test/ui_cocoa_test_helper.h" +#import "ui/views/cocoa/bridged_content_view.h" +#include "ui/views/ime/input_method.h" +#include "ui/views/view.h" + +namespace views { + +class BridgedNativeWidgetTest : public ui::CocoaTest { + public: + BridgedNativeWidgetTest(); + virtual ~BridgedNativeWidgetTest(); + + // testing::Test: + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; + + protected: + // TODO(tapted): Make this a EventCountView from widget_unittest.cc. + scoped_ptr<views::View> view_; + scoped_ptr<BridgedNativeWidget> bridge_; + + private: + DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidgetTest); +}; + +BridgedNativeWidgetTest::BridgedNativeWidgetTest() { +} + +BridgedNativeWidgetTest::~BridgedNativeWidgetTest() { +} + +void BridgedNativeWidgetTest::SetUp() { + ui::CocoaTest::SetUp(); + + view_.reset(new views::View); + bridge_.reset(new BridgedNativeWidget); + base::scoped_nsobject<NSWindow> window([test_window() retain]); + bridge_->Init(window); + bridge_->SetRootView(view_.get()); + + [test_window() makePretendKeyWindowAndSetFirstResponder:bridge_->ns_view()]; +} + +void BridgedNativeWidgetTest::TearDown() { + [test_window() clearPretendKeyWindowAndFirstResponder]; + + bridge_.reset(); + view_.reset(); + + ui::CocoaTest::TearDown(); +} + +// The TEST_VIEW macro expects the view it's testing to have a superview. In +// these tests, the NSView bridge is a contentView, at the root. These mimic +// what TEST_VIEW usually does. +TEST_F(BridgedNativeWidgetTest, BridgedNativeWidgetTest_TestViewAddRemove) { + base::scoped_nsobject<BridgedContentView> view([bridge_->ns_view() retain]); + EXPECT_NSEQ([test_window() contentView], view); + EXPECT_NSEQ(test_window(), [view window]); + + // The superview of a contentView is an NSNextStepFrame. + EXPECT_TRUE([view superview]); + EXPECT_TRUE([view hostedView]); + + // Destroying the C++ bridge should remove references to any C++ objects in + // the ObjectiveC object, and remove it from the hierarchy. + bridge_.reset(); + EXPECT_FALSE([view hostedView]); + EXPECT_FALSE([view superview]); + EXPECT_FALSE([view window]); + EXPECT_FALSE([test_window() contentView]); +} + +TEST_F(BridgedNativeWidgetTest, BridgedNativeWidgetTest_TestViewDisplay) { + [bridge_->ns_view() display]; +} + +// Test that resizing the window resizes the root view appropriately. +TEST_F(BridgedNativeWidgetTest, ViewSizeTracksWindow) { + const int kTestNewWidth = 400; + const int kTestNewHeight = 300; + + // |test_window()| is borderless, so these should align. + NSSize window_size = [test_window() frame].size; + EXPECT_EQ(view_->width(), static_cast<int>(window_size.width)); + EXPECT_EQ(view_->height(), static_cast<int>(window_size.height)); + + // Make sure a resize actually occurs. + EXPECT_NE(kTestNewWidth, view_->width()); + EXPECT_NE(kTestNewHeight, view_->height()); + + [test_window() setFrame:NSMakeRect(0, 0, kTestNewWidth, kTestNewHeight) + display:NO]; + EXPECT_EQ(kTestNewWidth, view_->width()); + EXPECT_EQ(kTestNewHeight, view_->height()); +} + +TEST_F(BridgedNativeWidgetTest, CreateInputMethod) { + scoped_ptr<views::InputMethod> input_method(bridge_->CreateInputMethod()); + EXPECT_TRUE(input_method); +} + +TEST_F(BridgedNativeWidgetTest, GetHostInputMethod) { + EXPECT_TRUE(bridge_->GetHostInputMethod()); +} + +} // namespace views diff --git a/chromium/ui/views/color_chooser/color_chooser_view.cc b/chromium/ui/views/color_chooser/color_chooser_view.cc index 3c649f9718a..8c283da829d 100644 --- a/chromium/ui/views/color_chooser/color_chooser_view.cc +++ b/chromium/ui/views/color_chooser/color_chooser_view.cc @@ -32,18 +32,19 @@ const int kHueIndicatorSize = 5; const int kBorderWidth = 1; const int kTextfieldLengthInChars = 14; -string16 GetColorText(SkColor color) { - return ASCIIToUTF16(base::StringPrintf("#%02x%02x%02x", - SkColorGetR(color), - SkColorGetG(color), - SkColorGetB(color))); +base::string16 GetColorText(SkColor color) { + return base::ASCIIToUTF16(base::StringPrintf("#%02x%02x%02x", + SkColorGetR(color), + SkColorGetG(color), + SkColorGetB(color))); } -bool GetColorFromText(const string16& text, SkColor* result) { +bool GetColorFromText(const base::string16& text, SkColor* result) { if (text.size() != 6 && !(text.size() == 7 && text[0] == '#')) return false; - std::string input = UTF16ToUTF8((text.size() == 6) ? text : text.substr(1)); + std::string input = + base::UTF16ToUTF8((text.size() == 6) ? text : text.substr(1)); std::vector<uint8> hex; if (!base::HexStringToBytes(input, &hex)) return false; @@ -125,7 +126,7 @@ class ColorChooserView::HueView : public LocatedEventHandlerView { virtual void ProcessEventAtLocation(const gfx::Point& point) OVERRIDE; // View overrides: - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; ColorChooserView* chooser_view_; @@ -163,7 +164,7 @@ void ColorChooserView::HueView::ProcessEventAtLocation( SchedulePaint(); } -gfx::Size ColorChooserView::HueView::GetPreferredSize() { +gfx::Size ColorChooserView::HueView::GetPreferredSize() const { // We put indicators on the both sides of the hue bar. return gfx::Size(kHueBarWidth + kHueIndicatorSize * 2 + kBorderWidth * 2, kSaturationValueSize + kBorderWidth * 2); @@ -237,7 +238,7 @@ class ColorChooserView::SaturationValueView : public LocatedEventHandlerView { virtual void ProcessEventAtLocation(const gfx::Point& point) OVERRIDE; // View overrides: - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; ColorChooserView* chooser_view_; @@ -252,7 +253,7 @@ ColorChooserView::SaturationValueView::SaturationValueView( : chooser_view_(chooser_view), hue_(0) { SetFocusable(false); - set_border(Border::CreateSolidBorder(kBorderWidth, SK_ColorGRAY)); + SetBorder(Border::CreateSolidBorder(kBorderWidth, SK_ColorGRAY)); } void ColorChooserView::SaturationValueView::OnHueChanged(SkScalar hue) { @@ -291,7 +292,7 @@ void ColorChooserView::SaturationValueView::ProcessEventAtLocation( chooser_view_->OnSaturationValueChosen(saturation, value); } -gfx::Size ColorChooserView::SaturationValueView::GetPreferredSize() { +gfx::Size ColorChooserView::SaturationValueView::GetPreferredSize() const { return gfx::Size(kSaturationValueSize + kBorderWidth * 2, kSaturationValueSize + kBorderWidth * 2); } @@ -348,7 +349,7 @@ class ColorChooserView::SelectedColorPatchView : public views::View { ColorChooserView::SelectedColorPatchView::SelectedColorPatchView() { SetFocusable(false); SetVisible(true); - set_border(Border::CreateSolidBorder(kBorderWidth, SK_ColorGRAY)); + SetBorder(Border::CreateSolidBorder(kBorderWidth, SK_ColorGRAY)); } void ColorChooserView::SelectedColorPatchView::SetColor(SkColor color) { @@ -393,7 +394,7 @@ ColorChooserView::ColorChooserView(ColorChooserListener* listener, GridLayout::FILL, GridLayout::FILL, 1, GridLayout::USE_PREF, 0, 0); layout->StartRow(0, 0); textfield_ = new Textfield(); - textfield_->SetController(this); + textfield_->set_controller(this); textfield_->set_default_width_in_chars(kTextfieldLengthInChars); layout->AddView(textfield_); selected_color_patch_ = new SelectedColorPatchView(); @@ -454,7 +455,7 @@ View* ColorChooserView::GetContentsView() { } void ColorChooserView::ContentsChanged(Textfield* sender, - const string16& new_contents) { + const base::string16& new_contents) { SkColor color = SK_ColorBLACK; if (GetColorFromText(new_contents, &color)) { SkColorToHSV(color, hsv_); diff --git a/chromium/ui/views/color_chooser/color_chooser_view.h b/chromium/ui/views/color_chooser/color_chooser_view.h index c77e0b66c17..e7fda323c87 100644 --- a/chromium/ui/views/color_chooser/color_chooser_view.h +++ b/chromium/ui/views/color_chooser/color_chooser_view.h @@ -55,7 +55,7 @@ class VIEWS_EXPORT ColorChooserView : public WidgetDelegateView, // TextfieldController overrides: virtual void ContentsChanged(Textfield* sender, - const string16& new_contents) OVERRIDE; + const base::string16& new_contents) OVERRIDE; virtual bool HandleKeyEvent(Textfield* sender, const ui::KeyEvent& key_event) OVERRIDE; diff --git a/chromium/ui/views/controls/button/blue_button.cc b/chromium/ui/views/controls/button/blue_button.cc index 3e52afd896e..e3a9487ff6c 100644 --- a/chromium/ui/views/controls/button/blue_button.cc +++ b/chromium/ui/views/controls/button/blue_button.cc @@ -5,19 +5,13 @@ #include "ui/views/controls/button/blue_button.h" #include "grit/ui_resources.h" -#include "ui/base/accessibility/accessible_view_state.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/sys_color_change_listener.h" #include "ui/views/controls/button/label_button_border.h" namespace { -// Insets for the unified blue_button images. This assumes that the images -// are of a 9 grid, of 5x5 size each. -const int kBlueButtonInsets = 5; - -// Default text and shadow colors for the blue button. -const SkColor kBlueButtonTextColor = SK_ColorWHITE; +// Default shadow color for the blue button. const SkColor kBlueButtonShadowColor = SkColorSetRGB(0x53, 0x8C, 0xEA); } // namespace @@ -27,42 +21,11 @@ namespace views { // static const char BlueButton::kViewClassName[] = "views/BlueButton"; -BlueButton::BlueButton(ButtonListener* listener, const string16& text) +BlueButton::BlueButton(ButtonListener* listener, const base::string16& text) : LabelButton(listener, text) { // Inherit STYLE_BUTTON insets, minimum size, alignment, etc. SetStyle(STYLE_BUTTON); - - ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - const gfx::Insets insets(kBlueButtonInsets, - kBlueButtonInsets, - kBlueButtonInsets, - kBlueButtonInsets); - - LabelButtonBorder* button_border = static_cast<LabelButtonBorder*>(border()); - button_border->SetPainter(false, STATE_NORMAL, - Painter::CreateImagePainter( - *rb.GetImageSkiaNamed(IDR_BLUE_BUTTON_NORMAL), insets)); - button_border->SetPainter(false, STATE_HOVERED, - Painter::CreateImagePainter( - *rb.GetImageSkiaNamed(IDR_BLUE_BUTTON_HOVER), insets)); - button_border->SetPainter(false, STATE_PRESSED, - Painter::CreateImagePainter( - *rb.GetImageSkiaNamed(IDR_BLUE_BUTTON_PRESSED), insets)); - button_border->SetPainter(false, STATE_DISABLED, - Painter::CreateImagePainter( - *rb.GetImageSkiaNamed(IDR_BLUE_BUTTON_DISABLED), insets)); - button_border->SetPainter(true, STATE_NORMAL, - Painter::CreateImagePainter( - *rb.GetImageSkiaNamed(IDR_BLUE_BUTTON_FOCUSED_NORMAL), insets)); - button_border->SetPainter(true, STATE_HOVERED, - Painter::CreateImagePainter( - *rb.GetImageSkiaNamed(IDR_BLUE_BUTTON_FOCUSED_HOVER), insets)); - button_border->SetPainter(true, STATE_PRESSED, - Painter::CreateImagePainter( - *rb.GetImageSkiaNamed(IDR_BLUE_BUTTON_FOCUSED_PRESSED), insets)); - button_border->SetPainter(true, STATE_DISABLED, - Painter::CreateImagePainter( - *rb.GetImageSkiaNamed(IDR_BLUE_BUTTON_DISABLED), insets)); + UpdateThemedBorder(); } BlueButton::~BlueButton() {} @@ -70,10 +33,18 @@ BlueButton::~BlueButton() {} void BlueButton::ResetColorsFromNativeTheme() { LabelButton::ResetColorsFromNativeTheme(); if (!gfx::IsInvertedColorScheme()) { - for (size_t state = STATE_NORMAL; state < STATE_COUNT; ++state) - SetTextColor(static_cast<ButtonState>(state), kBlueButtonTextColor); - label()->SetShadowColors(kBlueButtonShadowColor, kBlueButtonShadowColor); - label()->SetShadowOffset(0, 1); + SetTextColor(STATE_NORMAL, GetNativeTheme()-> + GetSystemColor(ui::NativeTheme::kColorId_BlueButtonEnabledColor)); + SetTextColor(STATE_HOVERED, GetNativeTheme()-> + GetSystemColor(ui::NativeTheme::kColorId_BlueButtonHoverColor)); + SetTextColor(STATE_PRESSED, GetNativeTheme()-> + GetSystemColor(ui::NativeTheme::kColorId_BlueButtonHighlightColor)); + SetTextColor(STATE_DISABLED, GetNativeTheme()-> + GetSystemColor(ui::NativeTheme::kColorId_BlueButtonDisabledColor)); + + // TODO(estade): this is not great on system themes. + label()->set_shadows(gfx::ShadowValues(1, + gfx::ShadowValue(gfx::Point(0, 1), 0, kBlueButtonShadowColor))); } } @@ -81,4 +52,28 @@ const char* BlueButton::GetClassName() const { return BlueButton::kViewClassName; } +scoped_ptr<LabelButtonBorder> BlueButton::CreateDefaultBorder() const { + // Insets for splitting the images. + const gfx::Insets insets(5, 5, 5, 5); + scoped_ptr<LabelButtonBorder> button_border(new LabelButtonBorder(style())); + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + button_border->SetPainter(false, STATE_NORMAL, Painter::CreateImagePainter( + *rb.GetImageSkiaNamed(IDR_BLUE_BUTTON_NORMAL), insets)); + button_border->SetPainter(false, STATE_HOVERED, Painter::CreateImagePainter( + *rb.GetImageSkiaNamed(IDR_BLUE_BUTTON_HOVER), insets)); + button_border->SetPainter(false, STATE_PRESSED, Painter::CreateImagePainter( + *rb.GetImageSkiaNamed(IDR_BLUE_BUTTON_PRESSED), insets)); + button_border->SetPainter(false, STATE_DISABLED, Painter::CreateImagePainter( + *rb.GetImageSkiaNamed(IDR_BLUE_BUTTON_DISABLED), insets)); + button_border->SetPainter(true, STATE_NORMAL, Painter::CreateImagePainter( + *rb.GetImageSkiaNamed(IDR_BLUE_BUTTON_FOCUSED_NORMAL), insets)); + button_border->SetPainter(true, STATE_HOVERED, Painter::CreateImagePainter( + *rb.GetImageSkiaNamed(IDR_BLUE_BUTTON_FOCUSED_HOVER), insets)); + button_border->SetPainter(true, STATE_PRESSED, Painter::CreateImagePainter( + *rb.GetImageSkiaNamed(IDR_BLUE_BUTTON_FOCUSED_PRESSED), insets)); + button_border->SetPainter(true, STATE_DISABLED, Painter::CreateImagePainter( + *rb.GetImageSkiaNamed(IDR_BLUE_BUTTON_DISABLED), insets)); + return button_border.Pass(); +} + } // namespace views diff --git a/chromium/ui/views/controls/button/blue_button.h b/chromium/ui/views/controls/button/blue_button.h index a169fba85d1..8c83c007ec6 100644 --- a/chromium/ui/views/controls/button/blue_button.h +++ b/chromium/ui/views/controls/button/blue_button.h @@ -22,6 +22,7 @@ class VIEWS_EXPORT BlueButton : public LabelButton { // Overridden from LabelButton: virtual void ResetColorsFromNativeTheme() OVERRIDE; virtual const char* GetClassName() const OVERRIDE; + virtual scoped_ptr<LabelButtonBorder> CreateDefaultBorder() const OVERRIDE; DISALLOW_COPY_AND_ASSIGN(BlueButton); }; diff --git a/chromium/ui/views/controls/button/blue_button_unittest.cc b/chromium/ui/views/controls/button/blue_button_unittest.cc new file mode 100644 index 00000000000..908e747d24c --- /dev/null +++ b/chromium/ui/views/controls/button/blue_button_unittest.cc @@ -0,0 +1,59 @@ +// Copyright 2014 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/controls/button/blue_button.h" + +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/skia_util.h" +#include "ui/views/controls/button/label_button_border.h" +#include "ui/views/test/views_test_base.h" + +namespace views { + +namespace { + +class TestBlueButton : public BlueButton { + public: + TestBlueButton() : BlueButton(NULL, base::ASCIIToUTF16("foo")) {} + virtual ~TestBlueButton() {} + + using BlueButton::OnNativeThemeChanged; + + private: + DISALLOW_COPY_AND_ASSIGN(TestBlueButton); +}; + +} // namespace + +typedef ViewsTestBase BlueButtonTest; + +TEST_F(BlueButtonTest, Border) { + // Compared to a normal LabelButton... + LabelButton button(NULL, base::ASCIIToUTF16("foo")); + button.SetBoundsRect(gfx::Rect(gfx::Point(0, 0), button.GetPreferredSize())); + gfx::Canvas button_canvas(button.bounds().size(), 1.0, true); + button.border()->Paint(button, &button_canvas); + + // ... a special blue border should be used. + TestBlueButton blue_button; + blue_button.SetBoundsRect(gfx::Rect(gfx::Point(0, 0), + blue_button.GetPreferredSize())); + gfx::Canvas canvas(blue_button.bounds().size(), 1.0, true); + blue_button.border()->Paint(blue_button, &canvas); + EXPECT_EQ(button.GetText(), blue_button.GetText()); + EXPECT_FALSE(gfx::BitmapsAreEqual(button_canvas.ExtractImageRep().sk_bitmap(), + canvas.ExtractImageRep().sk_bitmap())); + + // Make sure it's still used after the native theme "changes". + blue_button.OnNativeThemeChanged(NULL); + gfx::Canvas canvas2(blue_button.bounds().size(), 1.0, true); + blue_button.border()->Paint(blue_button, &canvas2); + + EXPECT_TRUE(gfx::BitmapsAreEqual(canvas.ExtractImageRep().sk_bitmap(), + canvas2.ExtractImageRep().sk_bitmap())); +} + +} // namespace views diff --git a/chromium/ui/views/controls/button/button.cc b/chromium/ui/views/controls/button/button.cc index 96e2e27432c..4e7f51b5450 100644 --- a/chromium/ui/views/controls/button/button.cc +++ b/chromium/ui/views/controls/button/button.cc @@ -5,31 +5,47 @@ #include "ui/views/controls/button/button.h" #include "base/strings/utf_string_conversions.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" namespace views { //////////////////////////////////////////////////////////////////////////////// +// Button, static public: + +// static +Button::ButtonState Button::GetButtonStateFrom(ui::NativeTheme::State state) { + switch (state) { + case ui::NativeTheme::kDisabled: return Button::STATE_DISABLED; + case ui::NativeTheme::kHovered: return Button::STATE_HOVERED; + case ui::NativeTheme::kNormal: return Button::STATE_NORMAL; + case ui::NativeTheme::kPressed: return Button::STATE_PRESSED; + case ui::NativeTheme::kMaxState: NOTREACHED() << "Unknown state: " << state; + } + return Button::STATE_NORMAL; +} + +//////////////////////////////////////////////////////////////////////////////// // Button, public: Button::~Button() { } -void Button::SetTooltipText(const string16& tooltip_text) { +void Button::SetTooltipText(const base::string16& tooltip_text) { tooltip_text_ = tooltip_text; if (accessible_name_.empty()) accessible_name_ = tooltip_text_; TooltipTextChanged(); } -void Button::SetAccessibleName(const string16& name) { +void Button::SetAccessibleName(const base::string16& name) { accessible_name_ = name; } //////////////////////////////////////////////////////////////////////////////// // Button, View overrides: -bool Button::GetTooltipText(const gfx::Point& p, string16* tooltip) const { +bool Button::GetTooltipText(const gfx::Point& p, + base::string16* tooltip) const { if (tooltip_text_.empty()) return false; @@ -37,8 +53,8 @@ bool Button::GetTooltipText(const gfx::Point& p, string16* tooltip) const { return true; } -void Button::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON; +void Button::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_BUTTON; state->name = accessible_name_; } diff --git a/chromium/ui/views/controls/button/button.h b/chromium/ui/views/controls/button/button.h index d038015f870..37d639dd6a8 100644 --- a/chromium/ui/views/controls/button/button.h +++ b/chromium/ui/views/controls/button/button.h @@ -5,6 +5,7 @@ #ifndef UI_VIEWS_CONTROLS_BUTTON_BUTTON_H_ #define UI_VIEWS_CONTROLS_BUTTON_BUTTON_H_ +#include "ui/native_theme/native_theme.h" #include "ui/views/view.h" namespace views { @@ -42,21 +43,22 @@ class VIEWS_EXPORT Button : public View { enum ButtonStyle { STYLE_BUTTON = 0, STYLE_TEXTBUTTON, - STYLE_NATIVE_TEXTBUTTON, STYLE_COUNT, }; - void SetTooltipText(const string16& tooltip_text); + static ButtonState GetButtonStateFrom(ui::NativeTheme::State state); + + void SetTooltipText(const base::string16& tooltip_text); int tag() const { return tag_; } void set_tag(int tag) { tag_ = tag; } - void SetAccessibleName(const string16& name); + void SetAccessibleName(const base::string16& name); // Overridden from View: virtual bool GetTooltipText(const gfx::Point& p, - string16* tooltip) const OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + base::string16* tooltip) const OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; protected: // Construct the Button with a Listener. The listener can be NULL. This can be @@ -72,10 +74,10 @@ class VIEWS_EXPORT Button : public View { private: // The text shown in a tooltip. - string16 tooltip_text_; + base::string16 tooltip_text_; // Accessibility data. - string16 accessible_name_; + base::string16 accessible_name_; // The id tag associated with this button. Used to disambiguate buttons in // the ButtonListener implementation. diff --git a/chromium/ui/views/controls/button/checkbox.cc b/chromium/ui/views/controls/button/checkbox.cc index ec33a47e2b1..22c5f88e2d6 100644 --- a/chromium/ui/views/controls/button/checkbox.cc +++ b/chromium/ui/views/controls/button/checkbox.cc @@ -5,7 +5,7 @@ #include "ui/views/controls/button/checkbox.h" #include "grit/ui_resources.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/base/resource/resource_bundle.h" #include "ui/views/controls/button/label_button_border.h" #include "ui/views/painter.h" @@ -15,15 +15,16 @@ namespace views { // static const char Checkbox::kViewClassName[] = "Checkbox"; -Checkbox::Checkbox(const string16& label) +Checkbox::Checkbox(const base::string16& label) : LabelButton(NULL, label), checked_(false) { SetHorizontalAlignment(gfx::ALIGN_LEFT); - LabelButtonBorder* button_border = static_cast<LabelButtonBorder*>(border()); + scoped_ptr<LabelButtonBorder> button_border(new LabelButtonBorder(style())); button_border->SetPainter(false, STATE_HOVERED, NULL); button_border->SetPainter(false, STATE_PRESSED, NULL); // Inset the trailing side by a couple pixels for the focus border. button_border->set_insets(gfx::Insets(0, 0, 0, 2)); + SetBorder(button_border.PassAs<Border>()); SetFocusable(true); ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); @@ -93,10 +94,11 @@ const char* Checkbox::GetClassName() const { return kViewClassName; } -void Checkbox::GetAccessibleState(ui::AccessibleViewState* state) { +void Checkbox::GetAccessibleState(ui::AXViewState* state) { LabelButton::GetAccessibleState(state); - state->role = ui::AccessibilityTypes::ROLE_CHECKBUTTON; - state->state = checked() ? ui::AccessibilityTypes::STATE_CHECKED : 0; + state->role = ui::AX_ROLE_CHECK_BOX; + if (checked()) + state->AddStateFlag(ui::AX_STATE_CHECKED); } void Checkbox::OnFocus() { diff --git a/chromium/ui/views/controls/button/checkbox.h b/chromium/ui/views/controls/button/checkbox.h index a603bd5b2dd..7bfd5bcf7d1 100644 --- a/chromium/ui/views/controls/button/checkbox.h +++ b/chromium/ui/views/controls/button/checkbox.h @@ -19,7 +19,7 @@ class VIEWS_EXPORT Checkbox : public LabelButton { public: static const char kViewClassName[]; - explicit Checkbox(const string16& label); + explicit Checkbox(const base::string16& label); virtual ~Checkbox(); // Sets a listener for this checkbox. Checkboxes aren't required to have them @@ -34,7 +34,7 @@ class VIEWS_EXPORT Checkbox : public LabelButton { // Overridden from LabelButton: virtual void Layout() OVERRIDE; virtual const char* GetClassName() const OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; virtual void OnFocus() OVERRIDE; virtual void OnBlur() OVERRIDE; virtual const gfx::ImageSkia& GetImage(ButtonState for_state) OVERRIDE; diff --git a/chromium/ui/views/controls/button/custom_button.cc b/chromium/ui/views/controls/button/custom_button.cc index 2be2a2301e1..0e237a31273 100644 --- a/chromium/ui/views/controls/button/custom_button.cc +++ b/chromium/ui/views/controls/button/custom_button.cc @@ -4,7 +4,7 @@ #include "ui/views/controls/button/custom_button.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/events/event.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/animation/throb_animation.h" @@ -101,7 +101,7 @@ void CustomButton::SetHotTracked(bool is_hot_tracked) { SetState(is_hot_tracked ? STATE_HOVERED : STATE_NORMAL); if (is_hot_tracked) - NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_FOCUS, true); + NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true); } bool CustomButton::IsHotTracked() const { @@ -199,6 +199,7 @@ bool CustomButton::OnKeyPressed(const ui::KeyEvent& event) { ui::MouseEvent synthetic_event(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); NotifyClick(synthetic_event); } else { @@ -216,6 +217,7 @@ bool CustomButton::OnKeyReleased(const ui::KeyEvent& event) { ui::MouseEvent synthetic_event(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); NotifyClick(synthetic_event); return true; @@ -260,6 +262,7 @@ bool CustomButton::AcceleratorPressed(const ui::Accelerator& accelerator) { ui::MouseEvent synthetic_event(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); NotifyClick(synthetic_event); return true; @@ -281,17 +284,17 @@ void CustomButton::OnDragDone() { SetState(STATE_NORMAL); } -void CustomButton::GetAccessibleState(ui::AccessibleViewState* state) { +void CustomButton::GetAccessibleState(ui::AXViewState* state) { Button::GetAccessibleState(state); switch (state_) { case STATE_HOVERED: - state->state = ui::AccessibilityTypes::STATE_HOTTRACKED; + state->AddStateFlag(ui::AX_STATE_HOVERED); break; case STATE_PRESSED: - state->state = ui::AccessibilityTypes::STATE_PRESSED; + state->AddStateFlag(ui::AX_STATE_PRESSED); break; case STATE_DISABLED: - state->state = ui::AccessibilityTypes::STATE_UNAVAILABLE; + state->AddStateFlag(ui::AX_STATE_DISABLED); break; case STATE_NORMAL: case STATE_COUNT: diff --git a/chromium/ui/views/controls/button/custom_button.h b/chromium/ui/views/controls/button/custom_button.h index 9ac502d3d96..4614a5c7323 100644 --- a/chromium/ui/views/controls/button/custom_button.h +++ b/chromium/ui/views/controls/button/custom_button.h @@ -18,8 +18,8 @@ namespace views { class CustomButtonStateChangedDelegate; -// A button with custom rendering. The common base class of ImageButton and -// TextButton. +// A button with custom rendering. The common base class of ImageButton, +// LabelButton and TextButton. // Note that this type of button is not focusable by default and will not be // part of the focus chain. Call SetFocusable(true) to make it part of the // focus chain. @@ -84,7 +84,7 @@ class VIEWS_EXPORT CustomButton : public Button, virtual void ShowContextMenu(const gfx::Point& p, ui::MenuSourceType source_type) OVERRIDE; virtual void OnDragDone() OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; virtual void VisibilityChanged(View* starting_from, bool is_visible) OVERRIDE; // Overridden from gfx::AnimationDelegate: diff --git a/chromium/ui/views/controls/button/custom_button_unittest.cc b/chromium/ui/views/controls/button/custom_button_unittest.cc index f7b11e648ed..84836716701 100644 --- a/chromium/ui/views/controls/button/custom_button_unittest.cc +++ b/chromium/ui/views/controls/button/custom_button_unittest.cc @@ -5,6 +5,9 @@ #include "ui/views/controls/button/custom_button.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/aura/test/test_cursor_client.h" +#include "ui/aura/window.h" +#include "ui/aura/window_event_dispatcher.h" #include "ui/base/layout.h" #include "ui/gfx/screen.h" #include "ui/views/controls/button/checkbox.h" @@ -16,12 +19,6 @@ #include "ui/views/controls/textfield/textfield.h" #include "ui/views/test/views_test_base.h" -#if defined(USE_AURA) -#include "ui/aura/root_window.h" -#include "ui/aura/test/test_cursor_client.h" -#include "ui/aura/window.h" -#endif - namespace views { namespace { @@ -38,7 +35,6 @@ class TestCustomButton : public CustomButton { DISALLOW_COPY_AND_ASSIGN(TestCustomButton); }; -#if defined(USE_AURA) void PerformGesture(CustomButton* button, ui::EventType event_type) { ui::GestureEventDetails gesture_details(event_type, 0, 0); base::TimeDelta time_stamp = base::TimeDelta::FromMicroseconds(0); @@ -46,7 +42,6 @@ void PerformGesture(CustomButton* button, ui::EventType event_type) { gesture_details, 1); button->OnGestureEvent(&gesture_event); } -#endif // USE_AURA } // namespace @@ -63,10 +58,8 @@ TEST_F(CustomButtonTest, HoverStateOnVisibilityChange) { widget->Init(params); widget->Show(); -#if defined(USE_AURA) aura::test::TestCursorClient cursor_client( widget->GetNativeView()->GetRootWindow()); -#endif // Position the widget in a way so that it is under the cursor. gfx::Point cursor = gfx::Screen::GetScreenFor( @@ -80,10 +73,12 @@ TEST_F(CustomButtonTest, HoverStateOnVisibilityChange) { gfx::Point center(10, 10); button->OnMousePressed(ui::MouseEvent(ui::ET_MOUSE_PRESSED, center, center, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)); EXPECT_EQ(CustomButton::STATE_PRESSED, button->state()); button->OnMouseReleased(ui::MouseEvent(ui::ET_MOUSE_RELEASED, center, center, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)); EXPECT_EQ(CustomButton::STATE_HOVERED, button->state()); @@ -99,7 +94,6 @@ TEST_F(CustomButtonTest, HoverStateOnVisibilityChange) { button->SetVisible(true); EXPECT_EQ(CustomButton::STATE_HOVERED, button->state()); -#if defined(USE_AURA) // In Aura views, no new hover effects are invoked if mouse events // are disabled. cursor_client.DisableMouseEvents(); @@ -115,10 +109,8 @@ TEST_F(CustomButtonTest, HoverStateOnVisibilityChange) { button->SetVisible(true); EXPECT_EQ(CustomButton::STATE_NORMAL, button->state()); -#endif } -#if defined(USE_AURA) // Tests that gesture events correctly change the button state. TEST_F(CustomButtonTest, GestureEventsSetState) { // Create a widget so that the CustomButton can query the hover state @@ -148,12 +140,10 @@ TEST_F(CustomButtonTest, GestureEventsSetState) { EXPECT_EQ(CustomButton::STATE_NORMAL, button->state()); } -#endif // USE_AURA - // Make sure all subclasses of CustomButton are correctly recognized // as CustomButton. TEST_F(CustomButtonTest, AsCustomButton) { - string16 text; + base::string16 text; TextButton text_button(NULL, text); EXPECT_TRUE(CustomButton::AsCustomButton(&text_button)); @@ -176,7 +166,7 @@ TEST_F(CustomButtonTest, AsCustomButton) { Link link(text); EXPECT_FALSE(CustomButton::AsCustomButton(&link)); - Textfield textfield(Textfield::STYLE_DEFAULT); + Textfield textfield; EXPECT_FALSE(CustomButton::AsCustomButton(&textfield)); } diff --git a/chromium/ui/views/controls/button/image_button.cc b/chromium/ui/views/controls/button/image_button.cc index 6a842346d2c..ecbd4a10724 100644 --- a/chromium/ui/views/controls/button/image_button.cc +++ b/chromium/ui/views/controls/button/image_button.cc @@ -5,7 +5,7 @@ #include "ui/views/controls/button/image_button.h" #include "base/strings/utf_string_conversions.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/gfx/animation/throb_animation.h" #include "ui/gfx/canvas.h" #include "ui/gfx/image/image_skia_operations.h" @@ -60,14 +60,6 @@ void ImageButton::SetBackground(SkColor color, *image, *mask); } -void ImageButton::SetOverlayImage(const gfx::ImageSkia* image) { - if (!image) { - overlay_image_ = gfx::ImageSkia(); - return; - } - overlay_image_ = *image; -} - void ImageButton::SetImageAlignment(HorizontalAlignment h_align, VerticalAlignment v_align) { h_alignment_ = h_align; @@ -82,7 +74,7 @@ void ImageButton::SetFocusPainter(scoped_ptr<Painter> focus_painter) { //////////////////////////////////////////////////////////////////////////////// // ImageButton, View overrides: -gfx::Size ImageButton::GetPreferredSize() { +gfx::Size ImageButton::GetPreferredSize() const { gfx::Size size = preferred_size_; if (!images_[STATE_NORMAL].isNull()) { size = gfx::Size(images_[STATE_NORMAL].width(), @@ -116,9 +108,6 @@ void ImageButton::OnPaint(gfx::Canvas* canvas) { canvas->DrawImageInt(background_image_, position.x(), position.y()); canvas->DrawImageInt(img, position.x(), position.y()); - - if (!overlay_image_.isNull()) - canvas->DrawImageInt(overlay_image_, position.x(), position.y()); } Painter::PaintFocusPainter(this, canvas, focus_painter()); @@ -206,7 +195,7 @@ void ToggleImageButton::SetToggled(bool toggled) { toggled_ = toggled; SchedulePaint(); - NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_VALUE_CHANGED, true); + NotifyAccessibilityEvent(ui::AX_EVENT_VALUE_CHANGED, true); } void ToggleImageButton::SetToggledImage(ButtonState state, @@ -220,7 +209,7 @@ void ToggleImageButton::SetToggledImage(ButtonState state, } } -void ToggleImageButton::SetToggledTooltipText(const string16& tooltip) { +void ToggleImageButton::SetToggledTooltipText(const base::string16& tooltip) { toggled_tooltip_text_ = tooltip; } @@ -249,7 +238,7 @@ void ToggleImageButton::SetImage(ButtonState state, // ToggleImageButton, View overrides: bool ToggleImageButton::GetTooltipText(const gfx::Point& p, - string16* tooltip) const { + base::string16* tooltip) const { if (!toggled_ || toggled_tooltip_text_.empty()) return Button::GetTooltipText(p, tooltip); @@ -257,7 +246,7 @@ bool ToggleImageButton::GetTooltipText(const gfx::Point& p, return true; } -void ToggleImageButton::GetAccessibleState(ui::AccessibleViewState* state) { +void ToggleImageButton::GetAccessibleState(ui::AXViewState* state) { ImageButton::GetAccessibleState(state); GetTooltipText(gfx::Point(), &state->name); } diff --git a/chromium/ui/views/controls/button/image_button.h b/chromium/ui/views/controls/button/image_button.h index 4977b06d398..fc02b8ac9ce 100644 --- a/chromium/ui/views/controls/button/image_button.h +++ b/chromium/ui/views/controls/button/image_button.h @@ -51,10 +51,6 @@ class VIEWS_EXPORT ImageButton : public CustomButton { const gfx::ImageSkia* image, const gfx::ImageSkia* mask); - // Set an |image| to draw on top of the normal / hot / pushed image. - // Pass NULL for no image. - void SetOverlayImage(const gfx::ImageSkia* image); - // Sets how the image is laid out within the button's bounds. void SetImageAlignment(HorizontalAlignment h_align, VerticalAlignment v_align); @@ -73,7 +69,7 @@ class VIEWS_EXPORT ImageButton : public CustomButton { } // Overridden from View: - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual const char* GetClassName() const OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; @@ -96,9 +92,6 @@ class VIEWS_EXPORT ImageButton : public CustomButton { gfx::ImageSkia background_image_; - // Image to draw on top of normal / hot / pushed image. Usually empty. - gfx::ImageSkia overlay_image_; - private: FRIEND_TEST_ALL_PREFIXES(ImageButtonTest, Basics); FRIEND_TEST_ALL_PREFIXES(ImageButtonTest, ImagePositionWithBorder); @@ -145,7 +138,7 @@ class VIEWS_EXPORT ToggleImageButton : public ImageButton { void SetToggledImage(ButtonState state, const gfx::ImageSkia* image); // Set the tooltip text displayed when the button is toggled. - void SetToggledTooltipText(const string16& tooltip); + void SetToggledTooltipText(const base::string16& tooltip); // Overridden from ImageButton: virtual const gfx::ImageSkia& GetImage(ButtonState state) const OVERRIDE; @@ -154,8 +147,8 @@ class VIEWS_EXPORT ToggleImageButton : public ImageButton { // Overridden from View: virtual bool GetTooltipText(const gfx::Point& p, - string16* tooltip) const OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + base::string16* tooltip) const OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; private: // The parent class's images_ member is used for the current images, @@ -168,7 +161,7 @@ class VIEWS_EXPORT ToggleImageButton : public ImageButton { // The parent class's tooltip_text_ is displayed when not toggled, and // this one is shown when toggled. - string16 toggled_tooltip_text_; + base::string16 toggled_tooltip_text_; DISALLOW_COPY_AND_ASSIGN(ToggleImageButton); }; diff --git a/chromium/ui/views/controls/button/image_button_unittest.cc b/chromium/ui/views/controls/button/image_button_unittest.cc index 995b2056de4..406d02c3ae2 100644 --- a/chromium/ui/views/controls/button/image_button_unittest.cc +++ b/chromium/ui/views/controls/button/image_button_unittest.cc @@ -60,25 +60,6 @@ TEST_F(ImageButtonTest, Basics) { EXPECT_FALSE(button.GetImageToPaint().isNull()); EXPECT_EQ(10, button.GetImageToPaint().width()); EXPECT_EQ(20, button.GetImageToPaint().height()); - - // Set an overlay image. - gfx::ImageSkia overlay_image = CreateTestImage(12, 22); - button.SetOverlayImage(&overlay_image); - EXPECT_EQ(12, button.overlay_image_.width()); - EXPECT_EQ(22, button.overlay_image_.height()); - - // By convention, preferred size doesn't change, even though pushed image - // is bigger. - EXPECT_EQ("10x20", button.GetPreferredSize().ToString()); - - // We're still painting the normal image. - EXPECT_FALSE(button.GetImageToPaint().isNull()); - EXPECT_EQ(10, button.GetImageToPaint().width()); - EXPECT_EQ(20, button.GetImageToPaint().height()); - - // Reset the overlay image. - button.SetOverlayImage(NULL); - EXPECT_TRUE(button.overlay_image_.isNull()); } TEST_F(ImageButtonTest, SetAndGetImage) { @@ -116,11 +97,11 @@ TEST_F(ImageButtonTest, ImagePositionWithBorder) { EXPECT_EQ(gfx::Point().ToString(), button.ComputeImagePaintPosition(image).ToString()); - button.set_border(views::Border::CreateEmptyBorder(10, 5, 0, 0)); + button.SetBorder(views::Border::CreateEmptyBorder(10, 5, 0, 0)); EXPECT_EQ(gfx::Point(5, 10).ToString(), button.ComputeImagePaintPosition(image).ToString()); - button.set_border(NULL); + button.SetBorder(Border::NullBorder()); button.SetBounds(0, 0, 50, 50); EXPECT_EQ(gfx::Point().ToString(), button.ComputeImagePaintPosition(image).ToString()); @@ -129,7 +110,7 @@ TEST_F(ImageButtonTest, ImagePositionWithBorder) { ImageButton::ALIGN_MIDDLE); EXPECT_EQ(gfx::Point(15, 10).ToString(), button.ComputeImagePaintPosition(image).ToString()); - button.set_border(views::Border::CreateEmptyBorder(10, 10, 0, 0)); + button.SetBorder(views::Border::CreateEmptyBorder(10, 10, 0, 0)); EXPECT_EQ(gfx::Point(20, 15).ToString(), button.ComputeImagePaintPosition(image).ToString()); } diff --git a/chromium/ui/views/controls/button/label_button.cc b/chromium/ui/views/controls/button/label_button.cc index 30db5d13d9a..c0399147775 100644 --- a/chromium/ui/views/controls/button/label_button.cc +++ b/chromium/ui/views/controls/button/label_button.cc @@ -8,14 +8,17 @@ #include "grit/ui_resources.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/animation/throb_animation.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/font_list.h" #include "ui/gfx/sys_color_change_listener.h" #include "ui/native_theme/native_theme.h" +#include "ui/views/background.h" #include "ui/views/controls/button/label_button_border.h" #include "ui/views/painter.h" #include "ui/views/window/dialog_delegate.h" -#if defined(OS_WIN) -#include "ui/native_theme/native_theme_win.h" +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) +#include "ui/views/linux_ui/linux_ui.h" #endif namespace { @@ -23,9 +26,11 @@ namespace { // The spacing between the icon and text. const int kSpacing = 5; +#if !(defined(OS_LINUX) && !defined(OS_CHROMEOS)) // Default text and shadow colors for STYLE_BUTTON. const SkColor kStyleButtonTextColor = SK_ColorBLACK; const SkColor kStyleButtonShadowColor = SK_ColorWHITE; +#endif } // namespace @@ -37,7 +42,7 @@ const int LabelButton::kHoverAnimationDurationMs = 170; // static const char LabelButton::kViewClassName[] = "LabelButton"; -LabelButton::LabelButton(ButtonListener* listener, const string16& text) +LabelButton::LabelButton(ButtonListener* listener, const base::string16& text) : CustomButton(listener), image_(new ImageView()), label_(new Label()), @@ -45,9 +50,11 @@ LabelButton::LabelButton(ButtonListener* listener, const string16& text) button_state_colors_(), explicitly_set_colors_(), is_default_(false), - style_(STYLE_TEXTBUTTON) { + style_(STYLE_TEXTBUTTON), + border_is_themed_border_(true) { SetAnimationDuration(kHoverAnimationDurationMs); SetText(text); + SetFontList(gfx::FontList()); AddChildView(image_); image_->set_interactive(false); @@ -75,11 +82,11 @@ void LabelButton::SetImage(ButtonState for_state, const gfx::ImageSkia& image) { UpdateImage(); } -const string16& LabelButton::GetText() const { +const base::string16& LabelButton::GetText() const { return label_->text(); } -void LabelButton::SetText(const string16& text) { +void LabelButton::SetText(const base::string16& text) { SetAccessibleName(text); label_->SetText(text); } @@ -93,6 +100,14 @@ void LabelButton::SetTextColor(ButtonState for_state, SkColor color) { explicitly_set_colors_[for_state] = true; } +void LabelButton::SetTextShadows(const gfx::ShadowValues& shadows) { + label_->set_shadows(shadows); +} + +void LabelButton::SetTextSubpixelRenderingEnabled(bool enabled) { + label_->set_subpixel_rendering_enabled(enabled); +} + bool LabelButton::GetTextMultiLine() const { return label_->is_multi_line(); } @@ -101,20 +116,27 @@ void LabelButton::SetTextMultiLine(bool text_multi_line) { label_->SetMultiLine(text_multi_line); } -const gfx::Font& LabelButton::GetFont() const { - return label_->font(); +const gfx::FontList& LabelButton::GetFontList() const { + return label_->font_list(); } -void LabelButton::SetFont(const gfx::Font& font) { - label_->SetFont(font); +void LabelButton::SetFontList(const gfx::FontList& font_list) { + cached_normal_font_list_ = font_list; + cached_bold_font_list_ = font_list.DeriveWithStyle( + font_list.GetFontStyle() | gfx::Font::BOLD); + + // STYLE_BUTTON uses bold text to indicate default buttons. + label_->SetFontList( + style_ == STYLE_BUTTON && is_default_ ? + cached_bold_font_list_ : cached_normal_font_list_); } -void LabelButton::SetElideBehavior(Label::ElideBehavior elide_behavior) { +void LabelButton::SetElideBehavior(gfx::ElideBehavior elide_behavior) { label_->SetElideBehavior(elide_behavior); } gfx::HorizontalAlignment LabelButton::GetHorizontalAlignment() const { - return label_->horizontal_alignment(); + return label_->GetHorizontalAlignment(); } void LabelButton::SetHorizontalAlignment(gfx::HorizontalAlignment alignment) { @@ -122,6 +144,10 @@ void LabelButton::SetHorizontalAlignment(gfx::HorizontalAlignment alignment) { InvalidateLayout(); } +void LabelButton::SetDirectionalityMode(gfx::DirectionalityMode mode) { + label_->set_directionality_mode(mode); +} + void LabelButton::SetIsDefault(bool is_default) { if (is_default == is_default_) return; @@ -131,20 +157,13 @@ void LabelButton::SetIsDefault(bool is_default) { // STYLE_BUTTON uses bold text to indicate default buttons. if (style_ == STYLE_BUTTON) { - int style = label_->font().GetStyle(); - style = is_default ? style | gfx::Font::BOLD : style & ~gfx::Font::BOLD; - label_->SetFont(label_->font().DeriveFont(0, style)); + label_->SetFontList( + is_default ? cached_bold_font_list_ : cached_normal_font_list_); } } void LabelButton::SetStyle(ButtonStyle style) { - // Use the new button style instead of the native button style. - // TODO(msw): Officialy deprecate and remove STYLE_NATIVE_TEXTBUTTON. - if (style == STYLE_NATIVE_TEXTBUTTON) - style = STYLE_BUTTON; - style_ = style; - set_border(new LabelButtonBorder(style)); // Inset the button focus rect from the actual border; roughly match Windows. if (style == STYLE_BUTTON) { SetFocusPainter(scoped_ptr<Painter>()); @@ -152,34 +171,33 @@ void LabelButton::SetStyle(ButtonStyle style) { SetFocusPainter(Painter::CreateDashedFocusPainterWithInsets( gfx::Insets(3, 3, 3, 3))); } - if (style == STYLE_BUTTON || style == STYLE_NATIVE_TEXTBUTTON) { + if (style == STYLE_BUTTON) { label_->SetHorizontalAlignment(gfx::ALIGN_CENTER); SetFocusable(true); } if (style == STYLE_BUTTON) set_min_size(gfx::Size(70, 33)); - // Invalidate the layout to pickup the new insets from the border. - InvalidateLayout(); - ResetColorsFromNativeTheme(); + + OnNativeThemeChanged(GetNativeTheme()); } void LabelButton::SetFocusPainter(scoped_ptr<Painter> focus_painter) { focus_painter_ = focus_painter.Pass(); } -gfx::Size LabelButton::GetPreferredSize() { +gfx::Size LabelButton::GetPreferredSize() const { // Use a temporary label copy for sizing to avoid calculation side-effects. - gfx::Font font = GetFont(); - Label label(GetText(), font); + Label label(GetText(), cached_normal_font_list_); + label.set_shadows(label_->shadows()); label.SetMultiLine(GetTextMultiLine()); if (style() == STYLE_BUTTON) { // Some text appears wider when rendered normally than when rendered bold. // Accommodate the widest, as buttons may show bold and shouldn't resize. const int current_width = label.GetPreferredSize().width(); - label.SetFont(font.DeriveFont(0, font.GetStyle() ^ gfx::Font::BOLD)); + label.SetFontList(cached_bold_font_list_); if (label.GetPreferredSize().width() < current_width) - label.SetFont(font); + label.SetFontList(cached_normal_font_list_); } // Resize multi-line labels given the current limited available width. @@ -197,7 +215,7 @@ gfx::Size LabelButton::GetPreferredSize() { size.Enlarge(image_size.width() + insets.width(), insets.height()); // Make the size at least as large as the minimum size needed by the border. - size.SetToMax(border()->GetMinimumSize()); + size.SetToMax(border() ? border()->GetMinimumSize() : gfx::Size()); // Increase the minimum size monotonically with the preferred size. size.SetToMax(min_size_); @@ -217,17 +235,17 @@ void LabelButton::Layout() { adjusted_alignment = (adjusted_alignment == gfx::ALIGN_LEFT) ? gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT; - gfx::Rect child_area(GetLocalBounds()); + gfx::Rect child_area(GetChildAreaBounds()); child_area.Inset(GetInsets()); gfx::Size image_size(image_->GetPreferredSize()); - image_size.set_width(std::min(image_size.width(), child_area.width())); - image_size.set_height(std::min(image_size.height(), child_area.height())); + image_size.SetToMin(child_area.size()); // The label takes any remaining width after sizing the image, unless both // views are centered. In that case, using the tighter preferred label width // avoids wasted space within the label that would look like awkward padding. - gfx::Size label_size(child_area.size()); + // Labels can paint over the full button height, including the border height. + gfx::Size label_size(child_area.width(), height()); if (!image_size.IsEmpty() && !label_size.IsEmpty()) { label_size.set_width( std::max(child_area.width() - image_size.width() - kSpacing, 0)); @@ -250,8 +268,8 @@ void LabelButton::Layout() { image_origin.Offset(child_area.width() - image_size.width(), 0); } - gfx::Point label_origin(child_area.origin()); - if (!image_size.IsEmpty() &&adjusted_alignment != gfx::ALIGN_RIGHT) + gfx::Point label_origin(child_area.x(), 0); + if (!image_size.IsEmpty() && adjusted_alignment != gfx::ALIGN_RIGHT) label_origin.set_x(image_origin.x() + image_size.width() + kSpacing); image_->SetBoundsRect(gfx::Rect(image_origin, image_size)); @@ -262,6 +280,19 @@ const char* LabelButton::GetClassName() const { return kViewClassName; } +scoped_ptr<LabelButtonBorder> LabelButton::CreateDefaultBorder() const { + return scoped_ptr<LabelButtonBorder>(new LabelButtonBorder(style_)); +} + +void LabelButton::SetBorder(scoped_ptr<Border> border) { + border_is_themed_border_ = false; + View::SetBorder(border.Pass()); +} + +gfx::Rect LabelButton::GetChildAreaBounds() { + return GetLocalBounds(); +} + void LabelButton::OnPaint(gfx::Canvas* canvas) { View::OnPaint(canvas); Painter::PaintFocusPainter(this, canvas, focus_painter_.get()); @@ -284,9 +315,9 @@ void LabelButton::GetExtraParams(ui::NativeTheme::ExtraParams* params) const { params->button.indeterminate = false; params->button.is_default = is_default_; params->button.is_focused = HasFocus() && IsAccessibilityFocusable(); - params->button.has_border = style() == STYLE_NATIVE_TEXTBUTTON; + params->button.has_border = false; params->button.classic_state = 0; - params->button.background_color = label()->background_color(); + params->button.background_color = label_->background_color(); } void LabelButton::ResetColorsFromNativeTheme() { @@ -300,26 +331,31 @@ void LabelButton::ResetColorsFromNativeTheme() { // Certain styles do not change text color when hovered or pressed. bool constant_text_color = false; -#if defined(OS_WIN) - constant_text_color |= (style() == STYLE_NATIVE_TEXTBUTTON && - theme == ui::NativeThemeWin::instance()); -#endif - // Use hardcoded colors for inverted color scheme support and STYLE_BUTTON. if (gfx::IsInvertedColorScheme()) { constant_text_color = true; colors[STATE_NORMAL] = SK_ColorWHITE; label_->SetBackgroundColor(SK_ColorBLACK); + label_->set_background(Background::CreateSolidBackground(SK_ColorBLACK)); label_->SetAutoColorReadabilityEnabled(true); - label_->ClearEmbellishing(); + label_->set_shadows(gfx::ShadowValues()); } else if (style() == STYLE_BUTTON) { + // TODO(erg): This is disabled on desktop linux because of the binary asset + // confusion. These details should either be pushed into ui::NativeThemeWin + // or should be obsoleted by rendering buttons with paint calls instead of + // with static assets. http://crbug.com/350498 +#if !(defined(OS_LINUX) && !defined(OS_CHROMEOS)) constant_text_color = true; colors[STATE_NORMAL] = kStyleButtonTextColor; label_->SetBackgroundColor(theme->GetSystemColor( ui::NativeTheme::kColorId_ButtonBackgroundColor)); label_->SetAutoColorReadabilityEnabled(false); - label_->SetShadowColors(kStyleButtonShadowColor, kStyleButtonShadowColor); - label_->SetShadowOffset(0, 1); + label_->set_shadows(gfx::ShadowValues(1, + gfx::ShadowValue(gfx::Point(0, 1), 0, kStyleButtonShadowColor))); +#endif + label_->set_background(NULL); + } else { + label_->set_background(NULL); } if (constant_text_color) @@ -337,6 +373,27 @@ void LabelButton::UpdateImage() { image_->SetImage(GetImage(state())); } +void LabelButton::UpdateThemedBorder() { + // Don't override borders set by others. + if (!border_is_themed_border_) + return; + + scoped_ptr<LabelButtonBorder> label_button_border = CreateDefaultBorder(); + +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + views::LinuxUI* linux_ui = views::LinuxUI::instance(); + if (linux_ui) { + SetBorder(linux_ui->CreateNativeBorder( + this, label_button_border.Pass())); + } else +#endif + { + SetBorder(label_button_border.PassAs<Border>()); + } + + border_is_themed_border_ = true; +} + void LabelButton::StateChanged() { const gfx::Size previous_image_size(image_->GetPreferredSize()); UpdateImage(); @@ -354,6 +411,9 @@ void LabelButton::ChildPreferredSizeChanged(View* child) { void LabelButton::OnNativeThemeChanged(const ui::NativeTheme* theme) { ResetColorsFromNativeTheme(); + UpdateThemedBorder(); + // Invalidate the layout to pickup the new insets from the border. + InvalidateLayout(); } ui::NativeTheme::Part LabelButton::GetThemePart() const { @@ -378,13 +438,6 @@ ui::NativeTheme::State LabelButton::GetThemeState( } const gfx::Animation* LabelButton::GetThemeAnimation() const { -#if defined(OS_WIN) - if (style() == STYLE_NATIVE_TEXTBUTTON && - GetNativeTheme() == ui::NativeThemeWin::instance()) { - return ui::NativeThemeWin::instance()->IsThemingActive() ? - hover_animation_.get() : NULL; - } -#endif return hover_animation_.get(); } diff --git a/chromium/ui/views/controls/button/label_button.h b/chromium/ui/views/controls/button/label_button.h index ad7e226ad5e..80beb3c90d5 100644 --- a/chromium/ui/views/controls/button/label_button.h +++ b/chromium/ui/views/controls/button/label_button.h @@ -8,7 +8,6 @@ #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" #include "third_party/skia/include/core/SkColor.h" -#include "ui/gfx/font.h" #include "ui/gfx/image/image_skia.h" #include "ui/views/controls/button/custom_button.h" #include "ui/views/controls/image_view.h" @@ -17,6 +16,7 @@ namespace views { +class LabelButtonBorder; class Painter; // LabelButton is an alternative to TextButton, it's not focusable by default. @@ -28,7 +28,7 @@ class VIEWS_EXPORT LabelButton : public CustomButton, static const char kViewClassName[]; - LabelButton(ButtonListener* listener, const string16& text); + LabelButton(ButtonListener* listener, const base::string16& text); virtual ~LabelButton(); // Get or set the image shown for the specified button state. @@ -37,28 +37,37 @@ class VIEWS_EXPORT LabelButton : public CustomButton, void SetImage(ButtonState for_state, const gfx::ImageSkia& image); // Get or set the text shown on the button. - const string16& GetText() const; - void SetText(const string16& text); + const base::string16& GetText() const; + virtual void SetText(const base::string16& text); // Set the text color shown for the specified button state. void SetTextColor(ButtonState for_state, SkColor color); + // Set drop shadows underneath the text. + void SetTextShadows(const gfx::ShadowValues& shadows); + + // Sets whether subpixel rendering is used on the label. + void SetTextSubpixelRenderingEnabled(bool enabled); + // Get or set the text's multi-line property to break on '\n', etc. bool GetTextMultiLine() const; void SetTextMultiLine(bool text_multi_line); - // Get or set the font used by this button. - const gfx::Font& GetFont() const; - void SetFont(const gfx::Font& font); + // Get or set the font list used by this button. + const gfx::FontList& GetFontList() const; + void SetFontList(const gfx::FontList& font_list); // Set the elide behavior of this button. - void SetElideBehavior(Label::ElideBehavior elide_behavior); + void SetElideBehavior(gfx::ElideBehavior elide_behavior); // Get or set the horizontal alignment used for the button; reversed in RTL. // The optional image will lead the text, unless the button is right-aligned. gfx::HorizontalAlignment GetHorizontalAlignment() const; void SetHorizontalAlignment(gfx::HorizontalAlignment alignment); + // Set the directionality mode used for the button text. + void SetDirectionalityMode(gfx::DirectionalityMode mode); + // Call set_min_size(gfx::Size()) to clear the monotonically increasing size. void set_min_size(const gfx::Size& min_size) { min_size_ = min_size; } void set_max_size(const gfx::Size& max_size) { max_size_ = max_size; } @@ -72,9 +81,11 @@ class VIEWS_EXPORT LabelButton : public CustomButton, void SetStyle(ButtonStyle style); void SetFocusPainter(scoped_ptr<Painter> focus_painter); + Painter* focus_painter() { return focus_painter_.get(); } // View: - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual void SetBorder(scoped_ptr<Border> border) OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual void Layout() OVERRIDE; virtual const char* GetClassName() const OVERRIDE; @@ -82,10 +93,15 @@ class VIEWS_EXPORT LabelButton : public CustomButton, ImageView* image() const { return image_; } Label* label() const { return label_; } + // Returns the available area for the label and image. Subclasses can change + // these bounds if they need room to do manual painting. + virtual gfx::Rect GetChildAreaBounds(); + // View: virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; virtual void OnFocus() OVERRIDE; virtual void OnBlur() OVERRIDE; + virtual void OnNativeThemeChanged(const ui::NativeTheme* theme) OVERRIDE; // Fill |params| with information about the button. virtual void GetExtraParams(ui::NativeTheme::ExtraParams* params) const; @@ -93,9 +109,17 @@ class VIEWS_EXPORT LabelButton : public CustomButton, // Resets colors from the NativeTheme, explicitly set colors are unchanged. virtual void ResetColorsFromNativeTheme(); + // Creates the default border for this button. This can be overridden by + // subclasses or by LinuxUI. + virtual scoped_ptr<LabelButtonBorder> CreateDefaultBorder() const; + // Updates the image view to contain the appropriate button state image. void UpdateImage(); + // Updates the border as per the NativeTheme, unless a different border was + // set with SetBorder. + void UpdateThemedBorder(); + // NativeThemeDelegate: virtual gfx::Rect GetThemePaintRect() const OVERRIDE; @@ -104,14 +128,13 @@ class VIEWS_EXPORT LabelButton : public CustomButton, FRIEND_TEST_ALL_PREFIXES(LabelButtonTest, Label); FRIEND_TEST_ALL_PREFIXES(LabelButtonTest, Image); FRIEND_TEST_ALL_PREFIXES(LabelButtonTest, LabelAndImage); - FRIEND_TEST_ALL_PREFIXES(LabelButtonTest, Font); + FRIEND_TEST_ALL_PREFIXES(LabelButtonTest, FontList); // CustomButton: virtual void StateChanged() OVERRIDE; // View: virtual void ChildPreferredSizeChanged(View* child) OVERRIDE; - virtual void OnNativeThemeChanged(const ui::NativeTheme* theme) OVERRIDE; // NativeThemeDelegate: virtual ui::NativeTheme::Part GetThemePart() const OVERRIDE; @@ -127,6 +150,10 @@ class VIEWS_EXPORT LabelButton : public CustomButton, ImageView* image_; Label* label_; + // The cached font lists in the normal and bold style. + gfx::FontList cached_normal_font_list_; + gfx::FontList cached_bold_font_list_; + // The images and colors for each button state. gfx::ImageSkia button_state_images_[STATE_COUNT]; SkColor button_state_colors_[STATE_COUNT]; @@ -135,7 +162,7 @@ class VIEWS_EXPORT LabelButton : public CustomButton, bool explicitly_set_colors_[STATE_COUNT]; // |min_size_| increases monotonically with the preferred size. - gfx::Size min_size_; + mutable gfx::Size min_size_; // |max_size_| may be set to clamp the preferred size. gfx::Size max_size_; @@ -147,6 +174,9 @@ class VIEWS_EXPORT LabelButton : public CustomButton, // The button's overall style. ButtonStyle style_; + // True if current border was set by UpdateThemedBorder. Defaults to true. + bool border_is_themed_border_; + scoped_ptr<Painter> focus_painter_; DISALLOW_COPY_AND_ASSIGN(LabelButton); diff --git a/chromium/ui/views/controls/button/label_button_border.cc b/chromium/ui/views/controls/button/label_button_border.cc index d124764fd76..23e9319ded3 100644 --- a/chromium/ui/views/controls/button/label_button_border.cc +++ b/chromium/ui/views/controls/button/label_button_border.cc @@ -31,36 +31,20 @@ const int kButtonInsets = 5; const int kTextHoveredImages[] = IMAGE_GRID(IDR_TEXTBUTTON_HOVER); const int kTextPressedImages[] = IMAGE_GRID(IDR_TEXTBUTTON_PRESSED); -Button::ButtonState GetButtonState(ui::NativeTheme::State state) { - switch (state) { - case ui::NativeTheme::kDisabled: return Button::STATE_DISABLED; - case ui::NativeTheme::kHovered: return Button::STATE_HOVERED; - case ui::NativeTheme::kNormal: return Button::STATE_NORMAL; - case ui::NativeTheme::kPressed: return Button::STATE_PRESSED; - case ui::NativeTheme::kMaxState: NOTREACHED() << "Unknown state: " << state; - } - return Button::STATE_NORMAL; -} - -// A helper function to paint the native theme or images as appropriate. +// A helper function to paint the appropriate broder images. void PaintHelper(LabelButtonBorder* border, gfx::Canvas* canvas, - const ui::NativeTheme* theme, - ui::NativeTheme::Part part, ui::NativeTheme::State state, const gfx::Rect& rect, const ui::NativeTheme::ExtraParams& extra) { - if (border->style() == Button::STYLE_NATIVE_TEXTBUTTON) { - theme->Paint(canvas->sk_canvas(), part, state, rect, extra); - } else { - Painter* painter = - border->GetPainter(extra.button.is_focused, GetButtonState(state)); - // Paint any corresponding unfocused painter if there is no focused painter. - if (!painter && extra.button.is_focused) - painter = border->GetPainter(false, GetButtonState(state)); - if (painter) - Painter::PaintPainterAt(canvas, painter, rect); - } + Painter* painter = + border->GetPainter(extra.button.is_focused, + Button::GetButtonStateFrom(state)); + // Paint any corresponding unfocused painter if there is no focused painter. + if (!painter && extra.button.is_focused) + painter = border->GetPainter(false, Button::GetButtonStateFrom(state)); + if (painter) + Painter::PaintPainterAt(canvas, painter, rect); } } // namespace @@ -105,8 +89,6 @@ LabelButtonBorder::LabelButtonBorder(Button::ButtonStyle style) Painter::CreateImageGridPainter(kTextHoveredImages)); SetPainter(false, Button::STATE_PRESSED, Painter::CreateImageGridPainter(kTextPressedImages)); - } else if (style == Button::STYLE_NATIVE_TEXTBUTTON) { - set_insets(gfx::Insets(5, 12, 5, 12)); } } @@ -115,10 +97,8 @@ LabelButtonBorder::~LabelButtonBorder() {} void LabelButtonBorder::Paint(const View& view, gfx::Canvas* canvas) { const NativeThemeDelegate* native_theme_delegate = static_cast<const LabelButton*>(&view); - ui::NativeTheme::Part part = native_theme_delegate->GetThemePart(); gfx::Rect rect(native_theme_delegate->GetThemePaintRect()); ui::NativeTheme::ExtraParams extra; - const ui::NativeTheme* theme = view.GetNativeTheme(); const gfx::Animation* animation = native_theme_delegate->GetThemeAnimation(); ui::NativeTheme::State state = native_theme_delegate->GetThemeState(&extra); @@ -127,7 +107,7 @@ void LabelButtonBorder::Paint(const View& view, gfx::Canvas* canvas) { const SkRect sk_rect = gfx::RectToSkRect(rect); canvas->sk_canvas()->saveLayer(&sk_rect, NULL); state = native_theme_delegate->GetBackgroundThemeState(&extra); - PaintHelper(this, canvas, theme, part, state, rect, extra); + PaintHelper(this, canvas, state, rect, extra); SkPaint paint; skia::RefPtr<SkXfermode> sk_lerp_xfer = @@ -135,18 +115,12 @@ void LabelButtonBorder::Paint(const View& view, gfx::Canvas* canvas) { paint.setXfermode(sk_lerp_xfer.get()); canvas->sk_canvas()->saveLayer(&sk_rect, &paint); state = native_theme_delegate->GetForegroundThemeState(&extra); - PaintHelper(this, canvas, theme, part, state, rect, extra); + PaintHelper(this, canvas, state, rect, extra); canvas->sk_canvas()->restore(); canvas->sk_canvas()->restore(); } else { - PaintHelper(this, canvas, theme, part, state, rect, extra); - } - - // For inverted color schemes, draw a solid fill with the button color. - if (gfx::IsInvertedColorScheme()) { - rect.Inset(insets_); - canvas->FillRect(rect, extra.button.background_color); + PaintHelper(this, canvas, state, rect, extra); } } diff --git a/chromium/ui/views/controls/button/label_button_unittest.cc b/chromium/ui/views/controls/button/label_button_unittest.cc index fe2d1f670c1..74f77b702e6 100644 --- a/chromium/ui/views/controls/button/label_button_unittest.cc +++ b/chromium/ui/views/controls/button/label_button_unittest.cc @@ -2,14 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "ui/views/controls/button/label_button.h" + #include "base/strings/utf_string_conversions.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/canvas.h" -#include "ui/gfx/font.h" +#include "ui/gfx/font_list.h" #include "ui/gfx/size.h" -#include "ui/views/controls/button/label_button.h" +#include "ui/gfx/text_utils.h" #include "ui/views/test/views_test_base.h" +using base::ASCIIToUTF16; + namespace { gfx::ImageSkia CreateTestImage(int width, int height) { @@ -26,7 +30,7 @@ namespace views { typedef ViewsTestBase LabelButtonTest; TEST_F(LabelButtonTest, Init) { - const string16 text(ASCIIToUTF16("abc")); + const base::string16 text(ASCIIToUTF16("abc")); LabelButton button(NULL, text); EXPECT_TRUE(button.GetImage(Button::STATE_NORMAL).isNull()); @@ -45,19 +49,19 @@ TEST_F(LabelButtonTest, Init) { } TEST_F(LabelButtonTest, Label) { - LabelButton button(NULL, string16()); + LabelButton button(NULL, base::string16()); EXPECT_TRUE(button.GetText().empty()); - const gfx::Font font; - const string16 short_text(ASCIIToUTF16("abcdefghijklm")); - const string16 long_text(ASCIIToUTF16("abcdefghijklmnopqrstuvwxyz")); - const int short_text_width = gfx::Canvas::GetStringWidth(short_text, font); - const int long_text_width = gfx::Canvas::GetStringWidth(long_text, font); + const gfx::FontList font_list; + const base::string16 short_text(ASCIIToUTF16("abcdefghijklm")); + const base::string16 long_text(ASCIIToUTF16("abcdefghijklmnopqrstuvwxyz")); + const int short_text_width = gfx::GetStringWidth(short_text, font_list); + const int long_text_width = gfx::GetStringWidth(long_text, font_list); // The width increases monotonically with string size (it does not shrink). EXPECT_LT(button.GetPreferredSize().width(), short_text_width); button.SetText(short_text); - EXPECT_GT(button.GetPreferredSize().height(), font.GetHeight()); + EXPECT_GT(button.GetPreferredSize().height(), font_list.GetHeight()); EXPECT_GT(button.GetPreferredSize().width(), short_text_width); EXPECT_LT(button.GetPreferredSize().width(), long_text_width); button.SetText(long_text); @@ -76,7 +80,7 @@ TEST_F(LabelButtonTest, Label) { } TEST_F(LabelButtonTest, Image) { - LabelButton button(NULL, string16()); + LabelButton button(NULL, base::string16()); const int small_size = 50, large_size = 100; const gfx::ImageSkia small_image = CreateTestImage(small_size, small_size); @@ -108,15 +112,15 @@ TEST_F(LabelButtonTest, Image) { } TEST_F(LabelButtonTest, LabelAndImage) { - LabelButton button(NULL, string16()); + LabelButton button(NULL, base::string16()); - const gfx::Font font; - const string16 text(ASCIIToUTF16("abcdefghijklm")); - const int text_width = gfx::Canvas::GetStringWidth(text, font); + const gfx::FontList font_list; + const base::string16 text(ASCIIToUTF16("abcdefghijklm")); + const int text_width = gfx::GetStringWidth(text, font_list); const int image_size = 50; const gfx::ImageSkia image = CreateTestImage(image_size, image_size); - ASSERT_LT(font.GetHeight(), image_size); + ASSERT_LT(font_list.GetHeight(), image_size); // The width increases monotonically with content size (it does not shrink). EXPECT_LT(button.GetPreferredSize().width(), text_width); @@ -124,7 +128,7 @@ TEST_F(LabelButtonTest, LabelAndImage) { EXPECT_LT(button.GetPreferredSize().height(), image_size); button.SetText(text); EXPECT_GT(button.GetPreferredSize().width(), text_width); - EXPECT_GT(button.GetPreferredSize().height(), font.GetHeight()); + EXPECT_GT(button.GetPreferredSize().height(), font_list.GetHeight()); EXPECT_LT(button.GetPreferredSize().width(), text_width + image_size); EXPECT_LT(button.GetPreferredSize().height(), image_size); button.SetImage(Button::STATE_NORMAL, image); @@ -146,7 +150,7 @@ TEST_F(LabelButtonTest, LabelAndImage) { EXPECT_EQ(gfx::ALIGN_RIGHT, button.GetHorizontalAlignment()); EXPECT_LT(button.label_->bounds().right(), button.image_->bounds().x()); - button.SetText(string16()); + button.SetText(base::string16()); EXPECT_GT(button.GetPreferredSize().width(), text_width + image_size); EXPECT_GT(button.GetPreferredSize().height(), image_size); button.SetImage(Button::STATE_NORMAL, gfx::ImageSkia()); @@ -164,24 +168,25 @@ TEST_F(LabelButtonTest, LabelAndImage) { EXPECT_LT(button.GetPreferredSize().height(), image_size); } -TEST_F(LabelButtonTest, Font) { - const string16 text(ASCIIToUTF16("abc")); +TEST_F(LabelButtonTest, FontList) { + const base::string16 text(ASCIIToUTF16("abc")); LabelButton button(NULL, text); - const gfx::Font original_font = button.GetFont(); - const gfx::Font large_font = original_font.DeriveFont(100); + const gfx::FontList original_font_list = button.GetFontList(); + const gfx::FontList large_font_list = + original_font_list.DeriveWithSizeDelta(100); const int original_width = button.GetPreferredSize().width(); const int original_height = button.GetPreferredSize().height(); // The button size increases when the font size is increased. - button.SetFont(large_font); + button.SetFontList(large_font_list); EXPECT_GT(button.GetPreferredSize().width(), original_width); EXPECT_GT(button.GetPreferredSize().height(), original_height); // The button returns to its original size when the minimal size is cleared // and the original font size is restored. button.set_min_size(gfx::Size()); - button.SetFont(original_font); + button.SetFontList(original_font_list); EXPECT_EQ(original_width, button.GetPreferredSize().width()); EXPECT_EQ(original_height, button.GetPreferredSize().height()); } diff --git a/chromium/ui/views/controls/button/menu_button.cc b/chromium/ui/views/controls/button/menu_button.cc index 728c0371f1c..29271cea51f 100644 --- a/chromium/ui/views/controls/button/menu_button.cc +++ b/chromium/ui/views/controls/button/menu_button.cc @@ -7,7 +7,7 @@ #include "base/strings/utf_string_conversions.h" #include "grit/ui_resources.h" #include "grit/ui_strings.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" @@ -16,6 +16,7 @@ #include "ui/gfx/canvas.h" #include "ui/gfx/image/image.h" #include "ui/gfx/screen.h" +#include "ui/gfx/text_constants.h" #include "ui/views/controls/button/button.h" #include "ui/views/controls/button/menu_button_listener.h" #include "ui/views/mouse_constants.h" @@ -43,10 +44,10 @@ const int MenuButton::kMenuMarkerPaddingRight = -1; //////////////////////////////////////////////////////////////////////////////// MenuButton::MenuButton(ButtonListener* listener, - const string16& text, + const base::string16& text, MenuButtonListener* menu_button_listener, bool show_menu_marker) - : TextButton(listener, text), + : LabelButton(listener, text), menu_visible_(false), menu_offset_(kDefaultMenuOffsetX, kDefaultMenuOffsetY), listener_(menu_button_listener), @@ -54,7 +55,7 @@ MenuButton::MenuButton(ButtonListener* listener, menu_marker_(ui::ResourceBundle::GetSharedInstance().GetImageNamed( IDR_MENU_DROPARROW).ToImageSkia()), destroyed_flag_(NULL) { - set_alignment(TextButton::ALIGN_LEFT); + SetHorizontalAlignment(gfx::ALIGN_LEFT); } MenuButton::~MenuButton() { @@ -133,8 +134,8 @@ bool MenuButton::Activate() { return true; } -void MenuButton::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { - TextButton::PaintButton(canvas, mode); +void MenuButton::OnPaint(gfx::Canvas* canvas) { + LabelButton::OnPaint(canvas); if (show_menu_marker_) PaintMenuMarker(canvas); @@ -146,8 +147,8 @@ void MenuButton::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { // //////////////////////////////////////////////////////////////////////////////// -gfx::Size MenuButton::GetPreferredSize() { - gfx::Size prefsize = TextButton::GetPreferredSize(); +gfx::Size MenuButton::GetPreferredSize() const { + gfx::Size prefsize = LabelButton::GetPreferredSize(); if (show_menu_marker_) { prefsize.Enlarge(menu_marker_->width() + kMenuMarkerPaddingLeft + kMenuMarkerPaddingRight, @@ -186,7 +187,7 @@ void MenuButton::OnMouseReleased(const ui::MouseEvent& event) { HitTestPoint(event.location())) { Activate(); } else { - TextButton::OnMouseReleased(event); + LabelButton::OnMouseReleased(event); } } @@ -202,12 +203,14 @@ void MenuButton::OnMouseExited(const ui::MouseEvent& event) { } void MenuButton::OnGestureEvent(ui::GestureEvent* event) { - if (state() != STATE_DISABLED && event->type() == ui::ET_GESTURE_TAP) { - if (Activate()) - event->StopPropagation(); + if (state() != STATE_DISABLED && event->type() == ui::ET_GESTURE_TAP && + !Activate()) { + // When |Activate()| returns |false|, it means that a menu is shown and + // has handled the gesture event. So, there is no need to further process + // the gesture event here. return; } - TextButton::OnGestureEvent(event); + LabelButton::OnGestureEvent(event); } bool MenuButton::OnKeyPressed(const ui::KeyEvent& event) { @@ -220,16 +223,12 @@ bool MenuButton::OnKeyPressed(const ui::KeyEvent& event) { case ui::VKEY_UP: case ui::VKEY_DOWN: { // WARNING: we may have been deleted by the time Activate returns. - bool ret = Activate(); -#if defined(USE_AURA) - // This is to prevent the keyboard event from being dispatched twice. - // The Activate function returns false in most cases. In AURA if the - // keyboard event is not handled, we pass it to the default handler - // which dispatches the event back to us causing the menu to get - // displayed again. - ret = true; -#endif - return ret; + Activate(); + // This is to prevent the keyboard event from being dispatched twice. If + // the keyboard event is not handled, we pass it to the default handler + // which dispatches the event back to us causing the menu to get displayed + // again. Return true to prevent this. + return true; } default: break; @@ -244,11 +243,11 @@ bool MenuButton::OnKeyReleased(const ui::KeyEvent& event) { return false; } -void MenuButton::GetAccessibleState(ui::AccessibleViewState* state) { +void MenuButton::GetAccessibleState(ui::AXViewState* state) { CustomButton::GetAccessibleState(state); - state->role = ui::AccessibilityTypes::ROLE_BUTTONMENU; + state->role = ui::AX_ROLE_POP_UP_BUTTON; state->default_action = l10n_util::GetStringUTF16(IDS_APP_ACCACTION_PRESS); - state->state = ui::AccessibilityTypes::STATE_HASPOPUP; + state->AddStateFlag(ui::AX_STATE_HASPOPUP); } void MenuButton::PaintMenuMarker(gfx::Canvas* canvas) { @@ -267,6 +266,17 @@ void MenuButton::PaintMenuMarker(gfx::Canvas* canvas) { canvas->DrawImageInt(*menu_marker_, arrow_bounds.x(), arrow_bounds.y()); } +gfx::Rect MenuButton::GetChildAreaBounds() { + gfx::Size s = size(); + + if (show_menu_marker_) { + s.set_width(s.width() - menu_marker_->width() - kMenuMarkerPaddingLeft - + kMenuMarkerPaddingRight); + } + + return gfx::Rect(s); +} + int MenuButton::GetMaximumScreenXCoordinate() { if (!GetWidget()) { NOTREACHED(); diff --git a/chromium/ui/views/controls/button/menu_button.h b/chromium/ui/views/controls/button/menu_button.h index 2262dab4637..257d2f62ea9 100644 --- a/chromium/ui/views/controls/button/menu_button.h +++ b/chromium/ui/views/controls/button/menu_button.h @@ -9,9 +9,8 @@ #include "base/strings/string16.h" #include "base/time/time.h" -#include "ui/gfx/font.h" #include "ui/views/background.h" -#include "ui/views/controls/button/text_button.h" +#include "ui/views/controls/button/label_button.h" namespace views { @@ -24,7 +23,7 @@ class MenuButtonListener; // A button that shows a menu when the left mouse button is pushed // //////////////////////////////////////////////////////////////////////////////// -class VIEWS_EXPORT MenuButton : public TextButton { +class VIEWS_EXPORT MenuButton : public LabelButton { public: static const char kViewClassName[]; @@ -34,7 +33,7 @@ class VIEWS_EXPORT MenuButton : public TextButton { // Create a Button. MenuButton(ButtonListener* listener, - const string16& text, + const base::string16& text, MenuButtonListener* menu_button_listener, bool show_menu_marker); virtual ~MenuButton(); @@ -51,24 +50,25 @@ class VIEWS_EXPORT MenuButton : public TextButton { // Activate the button (called when the button is pressed). virtual bool Activate(); - // Overridden from TextButton for the potential use of a drop marker. - virtual void PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) OVERRIDE; - // Overridden from View: - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual const char* GetClassName() const OVERRIDE; + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE; virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE; virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE; virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE; virtual bool OnKeyReleased(const ui::KeyEvent& event) OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; protected: // Paint the menu marker image. void PaintMenuMarker(gfx::Canvas* canvas); + // Overridden from LabelButton: + virtual gfx::Rect GetChildAreaBounds() OVERRIDE; + // True if the menu is currently visible. bool menu_visible_; diff --git a/chromium/ui/views/controls/button/menu_button_unittest.cc b/chromium/ui/views/controls/button/menu_button_unittest.cc new file mode 100644 index 00000000000..6a18b21b104 --- /dev/null +++ b/chromium/ui/views/controls/button/menu_button_unittest.cc @@ -0,0 +1,199 @@ +// Copyright 2014 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/controls/button/menu_button.h" + +#include "base/strings/utf_string_conversions.h" +#include "ui/aura/test/event_generator.h" +#include "ui/aura/window.h" +#include "ui/views/controls/button/menu_button_listener.h" +#include "ui/views/test/views_test_base.h" + +using base::ASCIIToUTF16; + +namespace views { + +class MenuButtonTest : public ViewsTestBase { + public: + MenuButtonTest() : widget_(NULL), button_(NULL) {} + virtual ~MenuButtonTest() {} + + virtual void TearDown() OVERRIDE { + if (widget_ && !widget_->IsClosed()) + widget_->Close(); + + ViewsTestBase::TearDown(); + } + + Widget* widget() { return widget_; } + MenuButton* button() { return button_; } + + protected: + // Creates a MenuButton with a ButtonListener. In this case, the MenuButton + // acts like a regular button. + void CreateMenuButtonWithButtonListener(ButtonListener* button_listener) { + CreateWidget(); + + const base::string16 label(ASCIIToUTF16("button")); + button_ = new MenuButton(button_listener, label, NULL, false); + button_->SetBoundsRect(gfx::Rect(0, 0, 200, 20)); + widget_->SetContentsView(button_); + + widget_->Show(); + } + + // Creates a MenuButton with a MenuButtonListener. In this case, when the + // MenuButton is pushed, it notifies the MenuButtonListener to open a + // drop-down menu. + void CreateMenuButtonWithMenuButtonListener( + MenuButtonListener* menu_button_listener) { + CreateWidget(); + + const base::string16 label(ASCIIToUTF16("button")); + button_ = new MenuButton(NULL, label, menu_button_listener, false); + button_->SetBoundsRect(gfx::Rect(0, 0, 200, 20)); + widget_->SetContentsView(button_); + + widget_->Show(); + } + + private: + void CreateWidget() { + DCHECK(!widget_); + + widget_ = new Widget; + Widget::InitParams params = + CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); + params.bounds = gfx::Rect(0, 0, 200, 200); + widget_->Init(params); + } + + Widget* widget_; + MenuButton* button_; +}; + +class TestButtonListener : public ButtonListener { + public: + TestButtonListener() + : last_sender_(NULL), + last_sender_state_(Button::STATE_NORMAL), + last_event_type_(ui::ET_UNKNOWN) {} + virtual ~TestButtonListener() {} + + virtual void ButtonPressed(Button* sender, const ui::Event& event) OVERRIDE { + last_sender_ = sender; + CustomButton* custom_button = CustomButton::AsCustomButton(sender); + DCHECK(custom_button); + last_sender_state_ = custom_button->state(); + last_event_type_ = event.type(); + } + + Button* last_sender() { return last_sender_; } + Button::ButtonState last_sender_state() { return last_sender_state_; } + ui::EventType last_event_type() { return last_event_type_; } + + private: + Button* last_sender_; + Button::ButtonState last_sender_state_; + ui::EventType last_event_type_; + + DISALLOW_COPY_AND_ASSIGN(TestButtonListener); +}; + +class TestMenuButtonListener : public MenuButtonListener { + public: + TestMenuButtonListener() {} + virtual ~TestMenuButtonListener() {} + + virtual void OnMenuButtonClicked(View* source, + const gfx::Point& /*point*/) OVERRIDE { + last_source_ = source; + CustomButton* custom_button = CustomButton::AsCustomButton(source); + DCHECK(custom_button); + last_source_state_ = custom_button->state(); + } + + View* last_source() { return last_source_; } + Button::ButtonState last_source_state() { return last_source_state_; } + + private: + View* last_source_; + Button::ButtonState last_source_state_; +}; + +// Tests if the listener is notified correctly, when a mouse click happens on a +// MenuButton that has a regular ButtonListener. +TEST_F(MenuButtonTest, ActivateNonDropDownOnMouseClick) { + scoped_ptr<TestButtonListener> button_listener(new TestButtonListener); + CreateMenuButtonWithButtonListener(button_listener.get()); + + aura::test::EventGenerator generator( + widget()->GetNativeView()->GetRootWindow()); + + generator.set_current_location(gfx::Point(10, 10)); + generator.ClickLeftButton(); + + // Check that MenuButton has notified the listener on mouse-released event, + // while it was in hovered state. + EXPECT_EQ(button(), button_listener->last_sender()); + EXPECT_EQ(ui::ET_MOUSE_RELEASED, button_listener->last_event_type()); + EXPECT_EQ(Button::STATE_HOVERED, button_listener->last_sender_state()); +} + +// Tests if the listener is notified correctly when a gesture tap happens on a +// MenuButton that has a regular ButtonListener. +TEST_F(MenuButtonTest, ActivateNonDropDownOnGestureTap) { + scoped_ptr<TestButtonListener> button_listener(new TestButtonListener); + CreateMenuButtonWithButtonListener(button_listener.get()); + + aura::test::EventGenerator generator( + widget()->GetNativeView()->GetRootWindow()); + + generator.GestureTapAt(gfx::Point(10, 10)); + + // Check that MenuButton has notified the listener on gesture tap event, while + // it was in hovered state. + EXPECT_EQ(button(), button_listener->last_sender()); + EXPECT_EQ(ui::ET_GESTURE_TAP, button_listener->last_event_type()); + EXPECT_EQ(Button::STATE_HOVERED, button_listener->last_sender_state()); +} + +// Tests if the listener is notified correctly when a mouse click happens on a +// MenuButton that has a MenuButtonListener. +TEST_F(MenuButtonTest, ActivateDropDownOnMouseClick) { + scoped_ptr<TestMenuButtonListener> menu_button_listener( + new TestMenuButtonListener); + CreateMenuButtonWithMenuButtonListener(menu_button_listener.get()); + + aura::test::EventGenerator generator( + widget()->GetNativeView()->GetRootWindow()); + + generator.set_current_location(gfx::Point(10, 10)); + generator.ClickLeftButton(); + + // Check that MenuButton has notified the listener, while it was in pressed + // state. + EXPECT_EQ(button(), menu_button_listener->last_source()); + EXPECT_EQ(Button::STATE_PRESSED, menu_button_listener->last_source_state()); +} + +// Tests if the listener is notified correctly when a gesture tap happens on a +// MenuButton that has a MenuButtonListener. +TEST_F(MenuButtonTest, ActivateDropDownOnGestureTap) { + scoped_ptr<TestMenuButtonListener> menu_button_listener( + new TestMenuButtonListener); + CreateMenuButtonWithMenuButtonListener(menu_button_listener.get()); + + aura::test::EventGenerator generator( + widget()->GetNativeView()->GetRootWindow()); + + generator.GestureTapAt(gfx::Point(10, 10)); + + // Check that MenuButton has notified the listener, while it was in pressed + // state. + EXPECT_EQ(button(), menu_button_listener->last_source()); + EXPECT_EQ(Button::STATE_PRESSED, menu_button_listener->last_source_state()); +} + +} // namespace views diff --git a/chromium/ui/views/controls/button/radio_button.cc b/chromium/ui/views/controls/button/radio_button.cc index 224e9893873..a015d5e1da8 100644 --- a/chromium/ui/views/controls/button/radio_button.cc +++ b/chromium/ui/views/controls/button/radio_button.cc @@ -6,7 +6,7 @@ #include "base/logging.h" #include "grit/ui_resources.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/base/resource/resource_bundle.h" #include "ui/views/widget/widget.h" @@ -15,7 +15,7 @@ namespace views { // static const char RadioButton::kViewClassName[] = "RadioButton"; -RadioButton::RadioButton(const string16& label, int group_id) +RadioButton::RadioButton(const base::string16& label, int group_id) : Checkbox(label) { SetGroup(group_id); @@ -94,9 +94,9 @@ const char* RadioButton::GetClassName() const { return kViewClassName; } -void RadioButton::GetAccessibleState(ui::AccessibleViewState* state) { +void RadioButton::GetAccessibleState(ui::AXViewState* state) { Checkbox::GetAccessibleState(state); - state->role = ui::AccessibilityTypes::ROLE_RADIOBUTTON; + state->role = ui::AX_ROLE_RADIO_BUTTON; } View* RadioButton::GetSelectedViewForGroup(int group) { @@ -123,7 +123,7 @@ bool RadioButton::IsGroupFocusTraversable() const { void RadioButton::OnFocus() { Checkbox::OnFocus(); SetChecked(true); - ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), 0); + ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), 0, 0); LabelButton::NotifyClick(event); } diff --git a/chromium/ui/views/controls/button/radio_button.h b/chromium/ui/views/controls/button/radio_button.h index a7afff4ed41..edbb2c694cd 100644 --- a/chromium/ui/views/controls/button/radio_button.h +++ b/chromium/ui/views/controls/button/radio_button.h @@ -17,12 +17,12 @@ class VIEWS_EXPORT RadioButton : public Checkbox { // The button's class name. static const char kViewClassName[]; - RadioButton(const string16& label, int group_id); + RadioButton(const base::string16& label, int group_id); virtual ~RadioButton(); // Overridden from View: virtual const char* GetClassName() const OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; virtual View* GetSelectedViewForGroup(int group) OVERRIDE; virtual bool IsGroupFocusTraversable() const OVERRIDE; virtual void OnFocus() OVERRIDE; @@ -30,7 +30,7 @@ class VIEWS_EXPORT RadioButton : public Checkbox { // Overridden from Button: virtual void NotifyClick(const ui::Event& event) OVERRIDE; - // Overridden from TextButtonBase: + // Overridden from LabelButton: virtual ui::NativeTheme::Part GetThemePart() const OVERRIDE; // Overridden from Checkbox: diff --git a/chromium/ui/views/controls/button/text_button.cc b/chromium/ui/views/controls/button/text_button.cc index e829405b9f4..bf12f8e086d 100644 --- a/chromium/ui/views/controls/button/text_button.cc +++ b/chromium/ui/views/controls/button/text_button.cc @@ -88,14 +88,6 @@ void TextButtonBorder::SetInsets(const gfx::Insets& insets) { insets_ = insets; } -TextButtonBorder* TextButtonBorder::AsTextButtonBorder() { - return this; -} - -const TextButtonBorder* TextButtonBorder::AsTextButtonBorder() const { - return this; -} - // TextButtonDefaultBorder ---------------------------------------------------- @@ -199,15 +191,10 @@ void TextButtonNativeThemeBorder::Paint(const View& view, gfx::Canvas* canvas) { // TextButtonBase ------------------------------------------------------------- -TextButtonBase::TextButtonBase(ButtonListener* listener, const string16& text) +TextButtonBase::TextButtonBase(ButtonListener* listener, + const base::string16& text) : CustomButton(listener), alignment_(ALIGN_LEFT), - font_(ResourceBundle::GetSharedInstance().GetFont( - ResourceBundle::BaseFont)), - has_text_shadow_(false), - active_text_shadow_color_(0), - inactive_text_shadow_color_(0), - text_shadow_offset_(gfx::Point(1, 1)), min_width_(0), min_height_(0), max_width_(0), @@ -220,8 +207,6 @@ TextButtonBase::TextButtonBase(ButtonListener* listener, const string16& text) use_hover_color_from_theme_(true), focus_painter_(Painter::CreateDashedFocusPainter()) { SetText(text); - // OnNativeThemeChanged sets the color member variables. - TextButtonBase::OnNativeThemeChanged(GetNativeTheme()); SetAnimationDuration(kHoverAnimationDurationMs); } @@ -239,7 +224,7 @@ void TextButtonBase::SetIsDefault(bool is_default) { SchedulePaint(); } -void TextButtonBase::SetText(const string16& text) { +void TextButtonBase::SetText(const base::string16& text) { if (text == text_) return; text_ = text; @@ -247,8 +232,8 @@ void TextButtonBase::SetText(const string16& text) { UpdateTextSize(); } -void TextButtonBase::SetFont(const gfx::Font& font) { - font_ = font; +void TextButtonBase::SetFontList(const gfx::FontList& font_list) { + font_list_ = font_list; UpdateTextSize(); } @@ -274,21 +259,6 @@ void TextButtonBase::SetHoverColor(SkColor color) { use_hover_color_from_theme_ = false; } -void TextButtonBase::SetTextShadowColors(SkColor active_color, - SkColor inactive_color) { - active_text_shadow_color_ = active_color; - inactive_text_shadow_color_ = inactive_color; - has_text_shadow_ = true; -} - -void TextButtonBase::SetTextShadowOffset(int x, int y) { - text_shadow_offset_.SetPoint(x, y); -} - -void TextButtonBase::ClearEmbellishing() { - has_text_shadow_ = false; -} - void TextButtonBase::ClearMaxTextSize() { max_text_size_ = text_size_; } @@ -306,7 +276,7 @@ void TextButtonBase::SetMultiLine(bool multi_line) { } } -gfx::Size TextButtonBase::GetPreferredSize() { +gfx::Size TextButtonBase::GetPreferredSize() const { gfx::Insets insets = GetInsets(); // Use the max size to set the button boundaries. @@ -326,7 +296,7 @@ gfx::Size TextButtonBase::GetPreferredSize() { return prefsize; } -int TextButtonBase::GetHeightForWidth(int w) { +int TextButtonBase::GetHeightForWidth(int w) const { if (!multi_line_) return View::GetHeightForWidth(w); @@ -377,17 +347,51 @@ void TextButtonBase::UpdateTextSize() { } } -void TextButtonBase::CalculateTextSize(gfx::Size* text_size, int max_width) { - int h = font_.GetHeight(); +void TextButtonBase::CalculateTextSize(gfx::Size* text_size, + int max_width) const { + int h = font_list_.GetHeight(); int w = multi_line_ ? max_width : 0; int flags = ComputeCanvasStringFlags(); if (!multi_line_) flags |= gfx::Canvas::NO_ELLIPSIS; - gfx::Canvas::SizeStringInt(text_, font_, &w, &h, 0, flags); + gfx::Canvas::SizeStringInt(text_, font_list_, &w, &h, 0, flags); text_size->SetSize(w, h); } +void TextButtonBase::OnPaintText(gfx::Canvas* canvas, PaintButtonMode mode) { + gfx::Rect text_bounds(GetTextBounds()); + if (text_bounds.width() > 0) { + // Because the text button can (at times) draw multiple elements on the + // canvas, we can not mirror the button by simply flipping the canvas as + // doing this will mirror the text itself. Flipping the canvas will also + // make the icons look wrong because icons are almost always represented as + // direction-insensitive images and such images should never be flipped + // horizontally. + // + // Due to the above, we must perform the flipping manually for RTL UIs. + text_bounds.set_x(GetMirroredXForRect(text_bounds)); + + SkColor text_color = (show_multiple_icon_states_ && + (state() == STATE_HOVERED || state() == STATE_PRESSED)) ? + color_hover_ : color_; + + int draw_string_flags = gfx::Canvas::DefaultCanvasTextAlignment() | + ComputeCanvasStringFlags(); + + if (mode == PB_FOR_DRAG) { + // Disable sub-pixel rendering as background is transparent. + draw_string_flags |= gfx::Canvas::NO_SUBPIXEL_RENDERING; + canvas->DrawStringRectWithHalo(text_, font_list_, + SK_ColorBLACK, SK_ColorWHITE, + text_bounds, draw_string_flags); + } else { + canvas->DrawStringRectWithFlags(text_, font_list_, text_color, + text_bounds, draw_string_flags); + } + } +} + int TextButtonBase::ComputeCanvasStringFlags() const { if (!multi_line_) return 0; @@ -475,59 +479,10 @@ void TextButtonBase::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { Painter::PaintFocusPainter(this, canvas, focus_painter_.get()); } - gfx::Rect text_bounds(GetTextBounds()); - if (text_bounds.width() > 0) { - // Because the text button can (at times) draw multiple elements on the - // canvas, we can not mirror the button by simply flipping the canvas as - // doing this will mirror the text itself. Flipping the canvas will also - // make the icons look wrong because icons are almost always represented as - // direction-insensitive images and such images should never be flipped - // horizontally. - // - // Due to the above, we must perform the flipping manually for RTL UIs. - text_bounds.set_x(GetMirroredXForRect(text_bounds)); - - SkColor text_color = (show_multiple_icon_states_ && - (state() == STATE_HOVERED || state() == STATE_PRESSED)) ? - color_hover_ : color_; - - int draw_string_flags = gfx::Canvas::DefaultCanvasTextAlignment() | - ComputeCanvasStringFlags(); - - if (mode == PB_FOR_DRAG) { - // Disable sub-pixel rendering as background is transparent. - draw_string_flags |= gfx::Canvas::NO_SUBPIXEL_RENDERING; - -#if defined(OS_WIN) - // TODO(erg): Either port DrawStringWithHalo to linux or find an - // alternative here. - canvas->DrawStringWithHalo(text_, font_, SK_ColorBLACK, SK_ColorWHITE, - text_bounds.x(), text_bounds.y(), text_bounds.width(), - text_bounds.height(), draw_string_flags); -#else - canvas->DrawStringInt(text_, - font_, - text_color, - text_bounds.x(), - text_bounds.y(), - text_bounds.width(), - text_bounds.height(), - draw_string_flags); -#endif - } else { - gfx::ShadowValues shadows; - if (has_text_shadow_) { - SkColor color = GetWidget()->IsActive() ? active_text_shadow_color_ : - inactive_text_shadow_color_; - shadows.push_back(gfx::ShadowValue(text_shadow_offset_, 0, color)); - } - canvas->DrawStringWithShadows(text_, font_, text_color, text_bounds, - 0, draw_string_flags, shadows); - } - } + OnPaintText(canvas, mode); } -gfx::Size TextButtonBase::GetMinimumSize() { +gfx::Size TextButtonBase::GetMinimumSize() const { return max_text_size_; } @@ -609,14 +564,15 @@ ui::NativeTheme::State TextButtonBase::GetForegroundThemeState( // TextButton ----------------------------------------------------------------- -TextButton::TextButton(ButtonListener* listener, const string16& text) +TextButton::TextButton(ButtonListener* listener, const base::string16& text) : TextButtonBase(listener, text), icon_placement_(ICON_ON_LEFT), has_hover_icon_(false), has_pushed_icon_(false), icon_text_spacing_(kDefaultIconTextSpacing), - ignore_minimum_size_(true) { - set_border(new TextButtonDefaultBorder); + ignore_minimum_size_(true), + full_justification_(false) { + SetBorder(scoped_ptr<Border>(new TextButtonDefaultBorder)); SetFocusPainter(Painter::CreateDashedFocusPainterWithInsets( gfx::Insets(kFocusRectInset, kFocusRectInset, kFocusRectInset, kFocusRectInset))); @@ -642,7 +598,7 @@ void TextButton::SetPushedIcon(const gfx::ImageSkia& icon) { SchedulePaint(); } -gfx::Size TextButton::GetPreferredSize() { +gfx::Size TextButton::GetPreferredSize() const { gfx::Size prefsize(TextButtonBase::GetPreferredSize()); prefsize.Enlarge(icon_.width(), 0); prefsize.set_height(std::max(prefsize.height(), icon_.height())); @@ -657,8 +613,8 @@ gfx::Size TextButton::GetPreferredSize() { #if defined(OS_WIN) // Clamp the size returned to at least the minimum size. if (!ignore_minimum_size_) { - gfx::PlatformFontWin* platform_font = - static_cast<gfx::PlatformFontWin*>(font_.platform_font()); + gfx::PlatformFontWin* platform_font = static_cast<gfx::PlatformFontWin*>( + font_list_.GetPrimaryFont().platform_font()); prefsize.set_width(std::max( prefsize.width(), platform_font->horizontal_dlus_to_pixels(kMinWidthDLUs))); @@ -675,22 +631,37 @@ gfx::Size TextButton::GetPreferredSize() { } void TextButton::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { + if (full_justification_ && icon_placement_ == ICON_ON_LEFT) + set_alignment(ALIGN_RIGHT); + TextButtonBase::PaintButton(canvas, mode); + OnPaintIcon(canvas, mode); +} +void TextButton::OnPaintIcon(gfx::Canvas* canvas, PaintButtonMode mode) { const gfx::ImageSkia& icon = GetImageToPaint(); if (icon.width() > 0) { gfx::Rect text_bounds = GetTextBounds(); - int icon_x; + int icon_x = 0; int spacing = text_.empty() ? 0 : icon_text_spacing_; gfx::Insets insets = GetInsets(); - if (icon_placement_ == ICON_ON_LEFT) { - icon_x = text_bounds.x() - icon.width() - spacing; - } else if (icon_placement_ == ICON_ON_RIGHT) { - icon_x = text_bounds.right() + spacing; - } else { // ICON_CENTERED - DCHECK(text_.empty()); - icon_x = (width() - insets.width() - icon.width()) / 2 + insets.left(); + switch (icon_placement_) { + case ICON_ON_LEFT: + icon_x = full_justification_ ? insets.left() + : text_bounds.x() - icon.width() - spacing; + break; + case ICON_ON_RIGHT: + icon_x = full_justification_ ? width() - insets.right() - icon.width() + : text_bounds.right() + spacing; + break; + case ICON_CENTERED: + DCHECK(text_.empty()); + icon_x = (width() - insets.width() - icon.width()) / 2 + insets.left(); + break; + default: + NOTREACHED(); + break; } int available_height = height() - insets.height(); @@ -707,6 +678,10 @@ void TextButton::set_ignore_minimum_size(bool ignore_minimum_size) { ignore_minimum_size_ = ignore_minimum_size; } +void TextButton::set_full_justification(bool full_justification) { + full_justification_ = full_justification; +} + const char* TextButton::GetClassName() const { return kViewClassName; } diff --git a/chromium/ui/views/controls/button/text_button.h b/chromium/ui/views/controls/button/text_button.h index 4dc61577803..49c4f3f8c17 100644 --- a/chromium/ui/views/controls/button/text_button.h +++ b/chromium/ui/views/controls/button/text_button.h @@ -11,7 +11,7 @@ #include "base/memory/scoped_ptr.h" #include "base/strings/string16.h" #include "third_party/skia/include/core/SkColor.h" -#include "ui/gfx/font.h" +#include "ui/gfx/font_list.h" #include "ui/gfx/image/image_skia.h" #include "ui/views/border.h" #include "ui/views/controls/button/custom_button.h" @@ -35,10 +35,6 @@ class VIEWS_EXPORT TextButtonBorder : public Border { virtual gfx::Size GetMinimumSize() const OVERRIDE; private: - // Border: - virtual TextButtonBorder* AsTextButtonBorder() OVERRIDE; - virtual const TextButtonBorder* AsTextButtonBorder() const OVERRIDE; - gfx::Insets insets_; DISALLOW_COPY_AND_ASSIGN(TextButtonBorder); @@ -112,8 +108,8 @@ class VIEWS_EXPORT TextButtonBase : public CustomButton, // Call SetText once per string in your set of possible values at button // creation time, so that it can contain the largest of them and avoid // resizing the button when the text changes. - virtual void SetText(const string16& text); - const string16& text() const { return text_; } + virtual void SetText(const base::string16& text); + const base::string16& text() const { return text_; } enum TextAlignment { ALIGN_LEFT, @@ -143,24 +139,14 @@ class VIEWS_EXPORT TextButtonBase : public CustomButton, void set_min_width(int min_width) { min_width_ = min_width; } void set_min_height(int min_height) { min_height_ = min_height; } void set_max_width(int max_width) { max_width_ = max_width; } - void SetFont(const gfx::Font& font); - // Return the font used by this button. - gfx::Font font() const { return font_; } + const gfx::FontList& font_list() const { return font_list_; } + void SetFontList(const gfx::FontList& font_list); void SetEnabledColor(SkColor color); void SetDisabledColor(SkColor color); void SetHighlightColor(SkColor color); void SetHoverColor(SkColor color); - // Enables a drop shadow underneath the text. - void SetTextShadowColors(SkColor active_color, SkColor inactive_color); - - // Sets the drop shadow's offset from the text. - void SetTextShadowOffset(int x, int y); - - // Disables shadows. - void ClearEmbellishing(); - // Sets whether or not to show the hot and pushed states for the button icon // (if present) in addition to the normal state. Defaults to true. bool show_multiple_icon_states() const { return show_multiple_icon_states_; } @@ -175,9 +161,9 @@ class VIEWS_EXPORT TextButtonBase : public CustomButton, virtual void PaintButton(gfx::Canvas* canvas, PaintButtonMode mode); // Overridden from View: - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual gfx::Size GetMinimumSize() OVERRIDE; - virtual int GetHeightForWidth(int w) OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual gfx::Size GetMinimumSize() const OVERRIDE; + virtual int GetHeightForWidth(int w) const OVERRIDE; virtual void OnEnabledChanged() OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE; @@ -185,18 +171,23 @@ class VIEWS_EXPORT TextButtonBase : public CustomButton, virtual void OnNativeThemeChanged(const ui::NativeTheme* theme) OVERRIDE; protected: - TextButtonBase(ButtonListener* listener, const string16& text); + TextButtonBase(ButtonListener* listener, const base::string16& text); // Called when enabled or disabled state changes, or the colors for those // states change. virtual void UpdateColor(); // Updates text_size_ and max_text_size_ from the current text/font. This is - // invoked when the font or text changes. + // invoked when the font list or text changes. void UpdateTextSize(); // Calculate the size of the text size without setting any of the members. - void CalculateTextSize(gfx::Size* text_size, int max_width); + void CalculateTextSize(gfx::Size* text_size, int max_width) const; + + // Paint the button's text into the specified canvas. If |mode| is + // |PB_FOR_DRAG|, the function paints a drag image representation. Derived + // can override this function to change only the text rendering. + virtual void OnPaintText(gfx::Canvas* canvas, PaintButtonMode mode); void set_color_enabled(SkColor color) { color_enabled_ = color; } void set_color_disabled(SkColor color) { color_disabled_ = color; } @@ -239,10 +230,10 @@ class VIEWS_EXPORT TextButtonBase : public CustomButton, gfx::Rect GetContentBounds(int extra_width) const; // The text string that is displayed in the button. - string16 text_; + base::string16 text_; // The size of the text string. - gfx::Size text_size_; + mutable gfx::Size text_size_; // Track the size of the largest text string seen so far, so that // changing text_ will not resize the button boundary. @@ -251,16 +242,8 @@ class VIEWS_EXPORT TextButtonBase : public CustomButton, // The alignment of the text string within the button. TextAlignment alignment_; - // The font used to paint the text. - gfx::Font font_; - - // Flag indicating if a shadow should be drawn behind the text. - bool has_text_shadow_; - // Optional shadow text colors for active and inactive widget states. - SkColor active_text_shadow_color_; - SkColor inactive_text_shadow_color_; - // Space between the text and its shadow. Defaults to (1,1). - gfx::Point text_shadow_offset_; + // The font list used to paint the text. + gfx::FontList font_list_; // The dimensions of the button will be at least these values. int min_width_; @@ -310,7 +293,7 @@ class VIEWS_EXPORT TextButton : public TextButtonBase { // The button's class name. static const char kViewClassName[]; - TextButton(ButtonListener* listener, const string16& text); + TextButton(ButtonListener* listener, const base::string16& text); virtual ~TextButton(); void set_icon_text_spacing(int icon_text_spacing) { @@ -340,14 +323,21 @@ class VIEWS_EXPORT TextButton : public TextButtonBase { void set_ignore_minimum_size(bool ignore_minimum_size); + void set_full_justification(bool full_justification); + // Overridden from View: - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual const char* GetClassName() const OVERRIDE; // Overridden from TextButtonBase: virtual void PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) OVERRIDE; protected: + // Paint the button's icon into the specified canvas. If |mode| is + // |PB_FOR_DRAG|, the function paints a drag image representation. Derived + // can override this function to change only the icon rendering. + virtual void OnPaintIcon(gfx::Canvas* canvas, PaintButtonMode mode); + gfx::ImageSkia icon() const { return icon_; } virtual const gfx::ImageSkia& GetImageToPaint() const; @@ -382,6 +372,10 @@ class VIEWS_EXPORT TextButton : public TextButtonBase { // is true. Set to false to prevent narrower buttons. bool ignore_minimum_size_; + // True if the icon and the text are aligned along both the left and right + // margins of the button. + bool full_justification_; + DISALLOW_COPY_AND_ASSIGN(TextButton); }; diff --git a/chromium/ui/views/controls/combobox/combobox.cc b/chromium/ui/views/controls/combobox/combobox.cc index 7feb5ffd0a7..b2a456d4906 100644 --- a/chromium/ui/views/controls/combobox/combobox.cc +++ b/chromium/ui/views/controls/combobox/combobox.cc @@ -4,10 +4,12 @@ #include "ui/views/controls/combobox/combobox.h" +#include "base/bind.h" #include "base/logging.h" +#include "base/message_loop/message_loop_proxy.h" #include "base/strings/utf_string_conversions.h" #include "grit/ui_resources.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/base/models/combobox_model.h" #include "ui/base/resource/resource_bundle.h" #include "ui/events/event.h" @@ -16,6 +18,8 @@ #include "ui/gfx/canvas.h" #include "ui/gfx/image/image.h" #include "ui/gfx/scoped_canvas.h" +#include "ui/gfx/text_utils.h" +#include "ui/native_theme/common_theme.h" #include "ui/native_theme/native_theme.h" #include "ui/views/background.h" #include "ui/views/color_constants.h" @@ -23,6 +27,7 @@ #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/combobox/combobox_listener.h" #include "ui/views/controls/focusable_border.h" +#include "ui/views/controls/menu/menu_item_view.h" #include "ui/views/controls/menu/menu_runner.h" #include "ui/views/controls/menu/menu_runner_handler.h" #include "ui/views/controls/menu/submenu_view.h" @@ -53,8 +58,6 @@ const int kDisclosureArrowButtonRightPadding = 12; // Define the id of the first item in the menu (since it needs to be > 0) const int kFirstMenuItemId = 1000; -const SkColor kInvalidTextColor = SK_ColorWHITE; - // Used to indicate that no item is currently selected by the user. const int kNoSelection = -1; @@ -81,24 +84,6 @@ const int kFocusedPressedMenuButtonImages[] = #undef MENU_IMAGE_GRID -// The background to use for invalid comboboxes. -class InvalidBackground : public Background { - public: - InvalidBackground() {} - virtual ~InvalidBackground() {} - - // Overridden from Background: - virtual void Paint(gfx::Canvas* canvas, View* view) const OVERRIDE { - gfx::Rect bounds(view->GetLocalBounds()); - // Inset by 2 to leave 1 empty pixel between background and border. - bounds.Inset(2, 2, 2, 2); - canvas->FillRect(bounds, kWarningColor); - } - - private: - DISALLOW_COPY_AND_ASSIGN(InvalidBackground); -}; - // The transparent button which holds a button state but is not rendered. class TransparentButton : public CustomButton { public: @@ -108,6 +93,11 @@ class TransparentButton : public CustomButton { } virtual ~TransparentButton() {} + virtual bool OnMousePressed(const ui::MouseEvent& mouse_event) OVERRIDE { + parent()->RequestFocus(); + return true; + } + double GetAnimationValue() const { return hover_animation_->GetCurrentValue(); } @@ -238,15 +228,14 @@ const char Combobox::kViewClassName[] = "views/Combobox"; Combobox::Combobox(ui::ComboboxModel* model) : model_(model), - style_(STYLE_SHOW_DROP_DOWN_ON_CLICK), + style_(STYLE_NORMAL), listener_(NULL), selected_index_(model_->GetDefaultIndex()), invalid_(false), - disclosure_arrow_(ui::ResourceBundle::GetSharedInstance().GetImageNamed( - IDR_MENU_DROPARROW).ToImageSkia()), dropdown_open_(false), text_button_(new TransparentButton(this)), - arrow_button_(new TransparentButton(this)) { + arrow_button_(new TransparentButton(this)), + weak_ptr_factory_(this) { model_->AddObserver(this); UpdateFromModel(); SetFocusable(true); @@ -285,9 +274,9 @@ Combobox::~Combobox() { } // static -const gfx::Font& Combobox::GetFont() { +const gfx::FontList& Combobox::GetFontList() { ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - return rb.GetFont(ui::ResourceBundle::BaseFont); + return rb.GetFontList(ui::ResourceBundle::BaseFont); } void Combobox::SetStyle(Style style) { @@ -295,8 +284,11 @@ void Combobox::SetStyle(Style style) { return; style_ = style; + if (style_ == STYLE_ACTION) + selected_index_ = 0; UpdateBorder(); + UpdateFromModel(); PreferredSizeChanged(); } @@ -307,11 +299,17 @@ void Combobox::ModelChanged() { } void Combobox::SetSelectedIndex(int index) { + if (style_ == STYLE_ACTION) + return; + selected_index_ = index; SchedulePaint(); } bool Combobox::SelectValue(const base::string16& value) { + if (style_ == STYLE_ACTION) + return false; + for (int i = 0; i < model()->GetItemCount(); ++i) { if (value == model()->GetItemAt(i)) { SetSelectedIndex(i); @@ -321,7 +319,7 @@ bool Combobox::SelectValue(const base::string16& value) { return false; } -void Combobox::SetAccessibleName(const string16& name) { +void Combobox::SetAccessibleName(const base::string16& name) { accessible_name_ = name; } @@ -331,7 +329,6 @@ void Combobox::SetInvalid(bool invalid) { invalid_ = invalid; - set_background(invalid_ ? new InvalidBackground() : NULL); UpdateBorder(); SchedulePaint(); } @@ -350,13 +347,14 @@ void Combobox::Layout() { int arrow_button_width = 0; switch (style_) { - case STYLE_SHOW_DROP_DOWN_ON_CLICK: { + case STYLE_NORMAL: { arrow_button_width = width(); break; } - case STYLE_NOTIFY_ON_CLICK: { + case STYLE_ACTION: { arrow_button_width = GetDisclosureArrowLeftPadding() + - disclosure_arrow_->width() + GetDisclosureArrowRightPadding(); + ArrowSize().width() + + GetDisclosureArrowRightPadding(); text_button_width = width() - arrow_button_width; break; } @@ -377,10 +375,10 @@ bool Combobox::IsCommandEnabled(int id) const { void Combobox::ExecuteCommand(int id) { selected_index_ = MenuCommandToIndex(id); - OnSelectionChanged(); + OnPerformAction(); } -bool Combobox::GetAccelerator(int id, ui::Accelerator* accel) { +bool Combobox::GetAccelerator(int id, ui::Accelerator* accel) const { return false; } @@ -393,26 +391,27 @@ int Combobox::GetSelectedRow() { } void Combobox::SetSelectedRow(int row) { + int prev_index = selected_index_; SetSelectedIndex(row); + if (selected_index_ != prev_index) + OnPerformAction(); } -string16 Combobox::GetTextForRow(int row) { - return model()->IsItemSeparatorAt(row) ? string16() : model()->GetItemAt(row); +base::string16 Combobox::GetTextForRow(int row) { + return model()->IsItemSeparatorAt(row) ? base::string16() : + model()->GetItemAt(row); } //////////////////////////////////////////////////////////////////////////////// // Combobox, View overrides: -gfx::Size Combobox::GetPreferredSize() { - if (content_size_.IsEmpty()) - UpdateFromModel(); - +gfx::Size Combobox::GetPreferredSize() const { // The preferred size will drive the local bounds which in turn is used to set // the minimum width for the dropdown list. gfx::Insets insets = GetInsets(); int total_width = std::max(kMinComboboxWidth, content_size_.width()) + insets.width() + GetDisclosureArrowLeftPadding() + - disclosure_arrow_->width() + GetDisclosureArrowRightPadding(); + ArrowSize().width() + GetDisclosureArrowRightPadding(); return gfx::Size(total_width, content_size_.height() + insets.height()); } @@ -475,7 +474,7 @@ bool Combobox::OnKeyPressed(const ui::KeyEvent& e) { // Click the button only when the button style mode. case ui::VKEY_SPACE: - if (style_ == STYLE_NOTIFY_ON_CLICK) { + if (style_ == STYLE_ACTION) { // When pressing space, the click event will be raised after the key is // released. text_button_->SetState(Button::STATE_PRESSED); @@ -486,9 +485,9 @@ bool Combobox::OnKeyPressed(const ui::KeyEvent& e) { // Click the button only when the button style mode. case ui::VKEY_RETURN: - if (style_ != STYLE_NOTIFY_ON_CLICK) + if (style_ != STYLE_ACTION) return false; - HandleClickEvent(); + OnPerformAction(); break; default: @@ -498,34 +497,35 @@ bool Combobox::OnKeyPressed(const ui::KeyEvent& e) { if (show_menu) { UpdateFromModel(); ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD); - } else if (new_index != selected_index_ && new_index != kNoSelection) { + } else if (new_index != selected_index_ && new_index != kNoSelection && + style_ != STYLE_ACTION) { DCHECK(!model()->IsItemSeparatorAt(new_index)); selected_index_ = new_index; - OnSelectionChanged(); + OnPerformAction(); } return true; } bool Combobox::OnKeyReleased(const ui::KeyEvent& e) { - if (style_ != STYLE_NOTIFY_ON_CLICK) + if (style_ != STYLE_ACTION) return false; // crbug.com/127520 - if (e.key_code() == ui::VKEY_SPACE) - HandleClickEvent(); + if (e.key_code() == ui::VKEY_SPACE && style_ == STYLE_ACTION) + OnPerformAction(); return false; } void Combobox::OnPaint(gfx::Canvas* canvas) { switch (style_) { - case STYLE_SHOW_DROP_DOWN_ON_CLICK: { + case STYLE_NORMAL: { OnPaintBackground(canvas); PaintText(canvas); OnPaintBorder(canvas); break; } - case STYLE_NOTIFY_ON_CLICK: { + case STYLE_ACTION: { PaintButtons(canvas); PaintText(canvas); break; @@ -548,23 +548,27 @@ void Combobox::OnBlur() { SchedulePaint(); } -void Combobox::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_COMBOBOX; +void Combobox::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_COMBO_BOX; state->name = accessible_name_; state->value = model_->GetItemAt(selected_index_); state->index = selected_index_; state->count = model_->GetItemCount(); } -void Combobox::OnModelChanged() { +void Combobox::OnComboboxModelChanged(ui::ComboboxModel* model) { + DCHECK_EQ(model, model_); ModelChanged(); } void Combobox::ButtonPressed(Button* sender, const ui::Event& event) { + if (!enabled()) + return; + RequestFocus(); if (sender == text_button_) { - HandleClickEvent(); + OnPerformAction(); } else { DCHECK_EQ(arrow_button_, sender); // TODO(hajimehoshi): Fix the problem that the arrow button blinks when @@ -583,40 +587,50 @@ void Combobox::ButtonPressed(Button* sender, const ui::Event& event) { } void Combobox::UpdateFromModel() { - int max_width = 0; - const gfx::Font& font = Combobox::GetFont(); + const gfx::FontList& font_list = Combobox::GetFontList(); MenuItemView* menu = new MenuItemView(this); // MenuRunner owns |menu|. dropdown_list_menu_runner_.reset(new MenuRunner(menu)); int num_items = model()->GetItemCount(); + int width = 0; + bool text_item_appended = false; for (int i = 0; i < num_items; ++i) { + // When STYLE_ACTION is used, the first item and the following separators + // are not added to the dropdown menu. It is assumed that the first item is + // always selected and rendered on the top of the action button. if (model()->IsItemSeparatorAt(i)) { - menu->AppendSeparator(); + if (text_item_appended || style_ != STYLE_ACTION) + menu->AppendSeparator(); continue; } - string16 text = model()->GetItemAt(i); + base::string16 text = model()->GetItemAt(i); // Inserting the Unicode formatting characters if necessary so that the // text is displayed correctly in right-to-left UIs. base::i18n::AdjustStringForLocaleDirection(&text); - menu->AppendMenuItem(i + kFirstMenuItemId, text, MenuItemView::NORMAL); - max_width = std::max(max_width, font.GetStringWidth(text)); + if (style_ != STYLE_ACTION || i > 0) { + menu->AppendMenuItem(i + kFirstMenuItemId, text, MenuItemView::NORMAL); + text_item_appended = true; + } + + if (style_ != STYLE_ACTION || i == selected_index_) + width = std::max(width, gfx::GetStringWidth(text, font_list)); } - content_size_.SetSize(max_width, font.GetHeight()); + content_size_.SetSize(width, font_list.GetHeight()); } void Combobox::UpdateBorder() { - FocusableBorder* border = new FocusableBorder(); - if (style_ == STYLE_NOTIFY_ON_CLICK) + scoped_ptr<FocusableBorder> border(new FocusableBorder()); + if (style_ == STYLE_ACTION) border->SetInsets(8, 13, 8, 13); if (invalid_) border->SetColor(kWarningColor); - set_border(border); + SetBorder(border.PassAs<Border>()); } void Combobox::AdjustBoundsForRTLUI(gfx::Rect* rect) const { @@ -632,45 +646,52 @@ void Combobox::PaintText(gfx::Canvas* canvas) { int x = insets.left(); int y = insets.top(); int text_height = height() - insets.height(); - SkColor text_color = invalid() ? kInvalidTextColor : - GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_LabelEnabledColor); + SkColor text_color = GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_LabelEnabledColor); DCHECK_GE(selected_index_, 0); DCHECK_LT(selected_index_, model()->GetItemCount()); if (selected_index_ < 0 || selected_index_ > model()->GetItemCount()) selected_index_ = 0; - string16 text = model()->GetItemAt(selected_index_); + base::string16 text = model()->GetItemAt(selected_index_); - int disclosure_arrow_offset = width() - disclosure_arrow_->width() - + gfx::Size arrow_size = ArrowSize(); + int disclosure_arrow_offset = width() - arrow_size.width() - GetDisclosureArrowLeftPadding() - GetDisclosureArrowRightPadding(); - const gfx::Font& font = Combobox::GetFont(); - int text_width = font.GetStringWidth(text); + const gfx::FontList& font_list = Combobox::GetFontList(); + int text_width = gfx::GetStringWidth(text, font_list); if ((text_width + insets.width()) > disclosure_arrow_offset) text_width = disclosure_arrow_offset - insets.width(); gfx::Rect text_bounds(x, y, text_width, text_height); AdjustBoundsForRTLUI(&text_bounds); - canvas->DrawStringInt(text, font, text_color, text_bounds); + canvas->DrawStringRect(text, font_list, text_color, text_bounds); int arrow_x = disclosure_arrow_offset + GetDisclosureArrowLeftPadding(); gfx::Rect arrow_bounds(arrow_x, - height() / 2 - disclosure_arrow_->height() / 2, - disclosure_arrow_->width(), - disclosure_arrow_->height()); + height() / 2 - arrow_size.height() / 2, + arrow_size.width(), + arrow_size.height()); AdjustBoundsForRTLUI(&arrow_bounds); - SkPaint paint; - // This makes the arrow subtractive. - if (invalid()) - paint.setXfermodeMode(SkXfermode::kDstOut_Mode); - canvas->DrawImageInt(*disclosure_arrow_, arrow_bounds.x(), arrow_bounds.y(), - paint); + // TODO(estade): hack alert! Remove this direct call into CommonTheme. For now + // STYLE_ACTION isn't properly themed so we have to override the NativeTheme + // behavior. See crbug.com/384071 + if (style_ == STYLE_ACTION) { + ui::CommonThemePaintComboboxArrow(canvas->sk_canvas(), arrow_bounds); + } else { + ui::NativeTheme::ExtraParams ignored; + GetNativeTheme()->Paint(canvas->sk_canvas(), + ui::NativeTheme::kComboboxArrow, + ui::NativeTheme::kNormal, + arrow_bounds, + ignored); + } } void Combobox::PaintButtons(gfx::Canvas* canvas) { - DCHECK(style_ == STYLE_NOTIFY_ON_CLICK); + DCHECK(style_ == STYLE_ACTION); gfx::ScopedCanvas scoped_canvas(canvas); if (base::i18n::IsRTL()) { @@ -739,10 +760,12 @@ void Combobox::ShowDropDownMenu(ui::MenuSourceType source_type) { gfx::Rect lb = GetLocalBounds(); gfx::Point menu_position(lb.origin()); - // Inset the menu's requested position so the border of the menu lines up - // with the border of the combobox. - menu_position.set_x(menu_position.x() + kMenuBorderWidthLeft); - menu_position.set_y(menu_position.y() + kMenuBorderWidthTop); + if (style_ == STYLE_NORMAL) { + // Inset the menu's requested position so the border of the menu lines up + // with the border of the combobox. + menu_position.set_x(menu_position.x() + kMenuBorderWidthLeft); + menu_position.set_y(menu_position.y() + kMenuBorderWidthTop); + } lb.set_width(lb.width() - (kMenuBorderWidthLeft + kMenuBorderWidthRight)); View::ConvertPointToScreen(this, &menu_position); @@ -757,8 +780,10 @@ void Combobox::ShowDropDownMenu(ui::MenuSourceType source_type) { arrow_button_->SetState(Button::STATE_PRESSED); } dropdown_open_ = true; + MenuAnchorPosition anchor_position = + style_ == STYLE_ACTION ? MENU_ANCHOR_TOPRIGHT : MENU_ANCHOR_TOPLEFT; if (dropdown_list_menu_runner_->RunMenuAt(GetWidget(), NULL, bounds, - MenuItemView::TOPLEFT, source_type, + anchor_position, source_type, MenuRunner::COMBOBOX) == MenuRunner::MENU_DELETED) { return; @@ -774,12 +799,17 @@ void Combobox::ShowDropDownMenu(ui::MenuSourceType source_type) { SetMouseHandler(NULL); } -void Combobox::OnSelectionChanged() { - NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_VALUE_CHANGED, false); +void Combobox::OnPerformAction() { + NotifyAccessibilityEvent(ui::AX_EVENT_VALUE_CHANGED, false); SchedulePaint(); + + // This combobox may be deleted by the listener. + base::WeakPtr<Combobox> weak_ptr = weak_ptr_factory_.GetWeakPtr(); if (listener_) - listener_->OnSelectedIndexChanged(this); - // |this| may now be deleted. + listener_->OnPerformAction(this); + + if (weak_ptr && style_ == STYLE_ACTION) + selected_index_ = 0; } int Combobox::MenuCommandToIndex(int menu_command_id) const { @@ -792,9 +822,9 @@ int Combobox::MenuCommandToIndex(int menu_command_id) const { int Combobox::GetDisclosureArrowLeftPadding() const { switch (style_) { - case STYLE_SHOW_DROP_DOWN_ON_CLICK: + case STYLE_NORMAL: return kDisclosureArrowLeftPadding; - case STYLE_NOTIFY_ON_CLICK: + case STYLE_ACTION: return kDisclosureArrowButtonLeftPadding; } NOTREACHED(); @@ -803,21 +833,31 @@ int Combobox::GetDisclosureArrowLeftPadding() const { int Combobox::GetDisclosureArrowRightPadding() const { switch (style_) { - case STYLE_SHOW_DROP_DOWN_ON_CLICK: + case STYLE_NORMAL: return kDisclosureArrowRightPadding; - case STYLE_NOTIFY_ON_CLICK: + case STYLE_ACTION: return kDisclosureArrowButtonRightPadding; } NOTREACHED(); return 0; } -void Combobox::HandleClickEvent() { - if (style_ != STYLE_NOTIFY_ON_CLICK) - return; - - if (listener_) - listener_->OnComboboxTextButtonClicked(this); +gfx::Size Combobox::ArrowSize() const { +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + // TODO(estade): hack alert! This should always use GetNativeTheme(). For now + // STYLE_ACTION isn't properly themed so we have to override the NativeTheme + // behavior. See crbug.com/384071 + const ui::NativeTheme* native_theme_for_arrow = style_ == STYLE_ACTION ? + ui::NativeTheme::instance() : + GetNativeTheme(); +#else + const ui::NativeTheme* native_theme_for_arrow = GetNativeTheme(); +#endif + + ui::NativeTheme::ExtraParams ignored; + return native_theme_for_arrow->GetPartSize(ui::NativeTheme::kComboboxArrow, + ui::NativeTheme::kNormal, + ignored); } } // namespace views diff --git a/chromium/ui/views/controls/combobox/combobox.h b/chromium/ui/views/controls/combobox/combobox.h index 8ec45c9e211..22781a8ab1f 100644 --- a/chromium/ui/views/controls/combobox/combobox.h +++ b/chromium/ui/views/controls/combobox/combobox.h @@ -7,6 +7,7 @@ #include <string> +#include "base/memory/weak_ptr.h" #include "base/time/time.h" #include "ui/base/models/combobox_model_observer.h" #include "ui/gfx/animation/animation_delegate.h" @@ -16,7 +17,7 @@ #include "ui/views/controls/prefix_delegate.h" namespace gfx { -class Font; +class FontList; class SlideAnimation; } @@ -36,10 +37,14 @@ class Painter; class PrefixSelector; // A non-editable combobox (aka a drop-down list or selector). -// Combobox has two distinct parts, the drop down arrow and the text. When the -// user clicks on the text the drop down is either shown -// (STYLE_SHOW_DROP_DOWN_ON_CLICK) or the listener is notified -// (STYLE_NOTIFY_ON_CLICK). +// Combobox has two distinct parts, the drop down arrow and the text. Combobox +// offers two distinct behaviors: +// * STYLE_NORMAL: typical combobox, clicking on the text and/or button shows +// the drop down, arrow keys change selection, selected index can be changed by +// the user to something other than the first item. +// * STYLE_ACTION: clicking on the text notifies the listener. The menu can be +// shown only by clicking on the arrow. The selected index is always reverted to +// 0 after the listener is notified. class VIEWS_EXPORT Combobox : public MenuDelegate, public PrefixDelegate, public ui::ComboboxModelObserver, @@ -47,8 +52,8 @@ class VIEWS_EXPORT Combobox : public MenuDelegate, public: // The style of the combobox. enum Style { - STYLE_SHOW_DROP_DOWN_ON_CLICK, - STYLE_NOTIFY_ON_CLICK, + STYLE_NORMAL, + STYLE_ACTION, }; // The combobox's class name. @@ -58,7 +63,7 @@ class VIEWS_EXPORT Combobox : public MenuDelegate, explicit Combobox(ui::ComboboxModel* model); virtual ~Combobox(); - static const gfx::Font& GetFont(); + static const gfx::FontList& GetFontList(); // Sets the listener which will be called when a selection has been made. void set_listener(ComboboxListener* listener) { listener_ = listener; } @@ -79,7 +84,7 @@ class VIEWS_EXPORT Combobox : public MenuDelegate, ui::ComboboxModel* model() const { return model_; } // Set the accessible name of the combobox. - void SetAccessibleName(const string16& name); + void SetAccessibleName(const base::string16& name); // Visually marks the combobox as having an invalid value selected. // When invalid, it paints with white text on a red background. @@ -88,7 +93,7 @@ class VIEWS_EXPORT Combobox : public MenuDelegate, bool invalid() const { return invalid_; } // Overridden from View: - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual const char* GetClassName() const OVERRIDE; virtual bool SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) OVERRIDE; virtual bool OnKeyPressed(const ui::KeyEvent& e) OVERRIDE; @@ -96,7 +101,7 @@ class VIEWS_EXPORT Combobox : public MenuDelegate, virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; virtual void OnFocus() OVERRIDE; virtual void OnBlur() OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; virtual ui::TextInputClient* GetTextInputClient() OVERRIDE; virtual void Layout() OVERRIDE; @@ -104,23 +109,26 @@ class VIEWS_EXPORT Combobox : public MenuDelegate, virtual bool IsItemChecked(int id) const OVERRIDE; virtual bool IsCommandEnabled(int id) const OVERRIDE; virtual void ExecuteCommand(int id) OVERRIDE; - virtual bool GetAccelerator(int id, ui::Accelerator* accelerator) OVERRIDE; + virtual bool GetAccelerator(int id, + ui::Accelerator* accelerator) const OVERRIDE; // Overridden from PrefixDelegate: virtual int GetRowCount() OVERRIDE; virtual int GetSelectedRow() OVERRIDE; virtual void SetSelectedRow(int row) OVERRIDE; - virtual string16 GetTextForRow(int row) OVERRIDE; + virtual base::string16 GetTextForRow(int row) OVERRIDE; // Overriden from ComboboxModelObserver: - virtual void OnModelChanged() OVERRIDE; + virtual void OnComboboxModelChanged(ui::ComboboxModel* model) OVERRIDE; // Overriden from ButtonListener: virtual void ButtonPressed(Button* sender, const ui::Event& event) OVERRIDE; private: FRIEND_TEST_ALL_PREFIXES(ComboboxTest, Click); + FRIEND_TEST_ALL_PREFIXES(ComboboxTest, ClickButDisabled); FRIEND_TEST_ALL_PREFIXES(ComboboxTest, NotifyOnClickWithMouse); + FRIEND_TEST_ALL_PREFIXES(ComboboxTest, ContentWidth); // Updates the combobox's content from its model. void UpdateFromModel(); @@ -141,7 +149,9 @@ class VIEWS_EXPORT Combobox : public MenuDelegate, void ShowDropDownMenu(ui::MenuSourceType source_type); // Called when the selection is changed by the user. - void OnSelectionChanged(); + void OnPerformAction(); + void NotifyPerformAction(); + void AfterPerformAction(); // Converts a menu command ID to a menu item index. int MenuCommandToIndex(int menu_command_id) const; @@ -149,6 +159,9 @@ class VIEWS_EXPORT Combobox : public MenuDelegate, int GetDisclosureArrowLeftPadding() const; int GetDisclosureArrowRightPadding() const; + // Returns the size of the disclosure arrow. + gfx::Size ArrowSize() const; + // Handles the clicking event. void HandleClickEvent(); @@ -168,14 +181,11 @@ class VIEWS_EXPORT Combobox : public MenuDelegate, bool invalid_; // The accessible name of this combobox. - string16 accessible_name_; + base::string16 accessible_name_; // A helper used to select entries by keyboard input. scoped_ptr<PrefixSelector> selector_; - // The disclosure arrow next to the currently selected item from the list. - const gfx::ImageSkia* disclosure_arrow_; - // Responsible for showing the context menu. scoped_ptr<MenuRunner> dropdown_list_menu_runner_; @@ -191,7 +201,7 @@ class VIEWS_EXPORT Combobox : public MenuDelegate, base::Time closed_time_; // The maximum dimensions of the content in the dropdown - gfx::Size content_size_; + mutable gfx::Size content_size_; // The painters or images that are used when |style_| is STYLE_BUTTONS. The // first index means the state of unfocused or focused. @@ -209,6 +219,9 @@ class VIEWS_EXPORT Combobox : public MenuDelegate, CustomButton* text_button_; CustomButton* arrow_button_; + // Used for making calbacks. + base::WeakPtrFactory<Combobox> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(Combobox); }; diff --git a/chromium/ui/views/controls/combobox/combobox_listener.h b/chromium/ui/views/controls/combobox/combobox_listener.h index 2609f647023..2522e03c7d8 100644 --- a/chromium/ui/views/controls/combobox/combobox_listener.h +++ b/chromium/ui/views/controls/combobox/combobox_listener.h @@ -5,19 +5,24 @@ #ifndef UI_VIEWS_CONTROLS_COMBOBOX_COMBOBOX_LISTENER_H_ #define UI_VIEWS_CONTROLS_COMBOBOX_COMBOBOX_LISTENER_H_ +#include "ui/views/views_export.h" + namespace views { class Combobox; -// An interface implemented by an object to let it know that the selected index -// has changed. -class ComboboxListener { +// Interface used to notify consumers when something interesting happens to a +// Combobox. +class VIEWS_EXPORT ComboboxListener { public: - virtual void OnSelectedIndexChanged(Combobox* combobox) = 0; - - // Handles when the combobox's style is the button style and the button is - // clicked. - virtual void OnComboboxTextButtonClicked(Combobox* combobox) {} + // Invoked when the user does the appropriate gesture that some action should + // be performed. For both STYLE_NORMAL and STYLE_ACTION this is invoked if the + // user clicks on the menu button and then clicks an item. For STYLE_NORMAL + // this is also invoked when the menu is not showing and the does a gesture to + // change the selection (for example, presses the home or end keys). This is + // not invoked when the menu is shown and the user changes the selection + // without closing the menu. + virtual void OnPerformAction(Combobox* combobox) = 0; protected: virtual ~ComboboxListener() {} diff --git a/chromium/ui/views/controls/combobox/combobox_unittest.cc b/chromium/ui/views/controls/combobox/combobox_unittest.cc index 7b68c4578e3..67625a2aea1 100644 --- a/chromium/ui/views/controls/combobox/combobox_unittest.cc +++ b/chromium/ui/views/controls/combobox/combobox_unittest.cc @@ -8,8 +8,10 @@ #include "base/basictypes.h" #include "base/strings/utf_string_conversions.h" +#include "ui/base/ime/text_input_client.h" #include "ui/base/models/combobox_model.h" #include "ui/events/event.h" +#include "ui/events/event_constants.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/views/controls/combobox/combobox_listener.h" #include "ui/views/controls/menu/menu_runner.h" @@ -19,6 +21,8 @@ #include "ui/views/test/views_test_base.h" #include "ui/views/widget/widget.h" +using base::ASCIIToUTF16; + namespace views { namespace { @@ -27,15 +31,14 @@ namespace { // shown or not. class TestMenuRunnerHandler : public MenuRunnerHandler { public: - TestMenuRunnerHandler() - : executed_(false) {} + TestMenuRunnerHandler() : executed_(false) {} bool executed() const { return executed_; } virtual MenuRunner::RunResult RunMenuAt(Widget* parent, MenuButton* button, const gfx::Rect& bounds, - MenuItemView::AnchorPosition anchor, + MenuAnchorPosition anchor, ui::MenuSourceType source_type, int32 types) OVERRIDE { executed_ = true; @@ -55,8 +58,7 @@ class TestCombobox : public Combobox { explicit TestCombobox(ui::ComboboxModel* model) : Combobox(model), key_handled_(false), - key_received_(false) { - } + key_received_(false) {} virtual bool OnKeyPressed(const ui::KeyEvent& e) OVERRIDE { key_received_ = true; @@ -94,7 +96,7 @@ class TestComboboxModel : public ui::ComboboxModel { virtual int GetItemCount() const OVERRIDE { return 10; } - virtual string16 GetItemAt(int index) OVERRIDE { + virtual base::string16 GetItemAt(int index) OVERRIDE { if (IsItemSeparatorAt(index)) { NOTREACHED(); return ASCIIToUTF16("SEPARATOR"); @@ -115,13 +117,35 @@ class TestComboboxModel : public ui::ComboboxModel { DISALLOW_COPY_AND_ASSIGN(TestComboboxModel); }; +// A combobox model which refers to a vector. +class VectorComboboxModel : public ui::ComboboxModel { + public: + explicit VectorComboboxModel(std::vector<std::string>* values) + : values_(values) {} + virtual ~VectorComboboxModel() {} + + // ui::ComboboxModel: + virtual int GetItemCount() const OVERRIDE { + return (int)values_->size(); + } + virtual base::string16 GetItemAt(int index) OVERRIDE { + return ASCIIToUTF16(values_->at(index)); + } + virtual bool IsItemSeparatorAt(int index) OVERRIDE { + return false; + } + + private: + std::vector<std::string>* values_; +}; + class EvilListener : public ComboboxListener { public: - EvilListener() : deleted_(false) {}; + EvilListener() : deleted_(false) {} virtual ~EvilListener() {}; // ComboboxListener: - virtual void OnSelectedIndexChanged(Combobox* combobox) OVERRIDE { + virtual void OnPerformAction(Combobox* combobox) OVERRIDE { delete combobox; deleted_ = true; } @@ -136,31 +160,29 @@ class EvilListener : public ComboboxListener { class TestComboboxListener : public views::ComboboxListener { public: - TestComboboxListener() - : on_selected_index_changed_called_(false), - on_combobox_text_button_clicked_called_(false) { - } + TestComboboxListener() : perform_action_index_(-1), actions_performed_(0) {} virtual ~TestComboboxListener() {} - virtual void OnSelectedIndexChanged(views::Combobox* combobox) OVERRIDE { - on_selected_index_changed_called_ = true; + virtual void OnPerformAction(views::Combobox* combobox) OVERRIDE { + perform_action_index_ = combobox->selected_index(); + actions_performed_++; } - virtual void OnComboboxTextButtonClicked(views::Combobox* combobox) OVERRIDE { - on_combobox_text_button_clicked_called_ = true; + int perform_action_index() const { + return perform_action_index_; } - bool on_selected_index_changed_called() const { - return on_selected_index_changed_called_; + bool on_perform_action_called() const { + return actions_performed_ > 0; } - bool on_combobox_text_button_clicked_called() const { - return on_combobox_text_button_clicked_called_; + int actions_performed() const { + return actions_performed_; } private: - bool on_selected_index_changed_called_; - bool on_combobox_text_button_clicked_called_; + int perform_action_index_; + int actions_performed_; private: DISALLOW_COPY_AND_ASSIGN(TestComboboxListener); @@ -170,7 +192,7 @@ class TestComboboxListener : public views::ComboboxListener { class ComboboxTest : public ViewsTestBase { public: - ComboboxTest() : widget_(NULL), combobox_(NULL), input_method_(NULL) {} + ComboboxTest() : widget_(NULL), combobox_(NULL) {} virtual void TearDown() OVERRIDE { if (widget_) @@ -193,11 +215,10 @@ class ComboboxTest : public ViewsTestBase { widget_->SetContentsView(container); container->AddChildView(combobox_); - input_method_ = new MockInputMethod(); - widget_->ReplaceInputMethod(input_method_); + widget_->ReplaceInputMethod(new MockInputMethod); // Assumes the Widget is always focused. - input_method_->OnFocus(); + widget_->GetInputMethod()->OnFocus(); combobox_->RequestFocus(); combobox_->SizeToPreferredSize(); @@ -210,7 +231,7 @@ class ComboboxTest : public ViewsTestBase { void SendKeyEventWithType(ui::KeyboardCode key_code, ui::EventType type) { ui::KeyEvent event(type, key_code, 0, false); - input_method_->DispatchKeyEvent(event); + widget_->GetInputMethod()->DispatchKeyEvent(event); } View* GetFocusedView() { @@ -220,10 +241,12 @@ class ComboboxTest : public ViewsTestBase { void PerformClick(const gfx::Point& point) { ui::MouseEvent pressed_event = ui::MouseEvent(ui::ET_MOUSE_PRESSED, point, point, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); widget_->OnMouseEvent(&pressed_event); ui::MouseEvent released_event = ui::MouseEvent(ui::ET_MOUSE_RELEASED, point, point, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); widget_->OnMouseEvent(&released_event); } @@ -236,9 +259,6 @@ class ComboboxTest : public ViewsTestBase { // Combobox does not take ownership of the model, hence it needs to be scoped. scoped_ptr<TestComboboxModel> model_; - - // For testing input method related behaviors. - MockInputMethod* input_method_; }; TEST_F(ComboboxTest, KeyTest) { @@ -435,15 +455,48 @@ TEST_F(ComboboxTest, SelectValue) { EXPECT_EQ(1, combobox_->selected_index()); EXPECT_FALSE(combobox_->SelectValue(ASCIIToUTF16("BANANAS"))); EXPECT_EQ(1, combobox_->selected_index()); + + // With the action style, the selected index is always 0. + combobox_->SetStyle(Combobox::STYLE_ACTION); + EXPECT_FALSE(combobox_->SelectValue(ASCIIToUTF16("PEANUT BUTTER"))); + EXPECT_EQ(0, combobox_->selected_index()); + EXPECT_FALSE(combobox_->SelectValue(ASCIIToUTF16("JELLY"))); + EXPECT_EQ(0, combobox_->selected_index()); + EXPECT_FALSE(combobox_->SelectValue(ASCIIToUTF16("BANANAS"))); + EXPECT_EQ(0, combobox_->selected_index()); +} + +TEST_F(ComboboxTest, SelectIndexActionStyle) { + InitCombobox(); + + // With the action style, the selected index is always 0. + combobox_->SetStyle(Combobox::STYLE_ACTION); + combobox_->SetSelectedIndex(1); + EXPECT_EQ(0, combobox_->selected_index()); + combobox_->SetSelectedIndex(2); + EXPECT_EQ(0, combobox_->selected_index()); + combobox_->SetSelectedIndex(3); + EXPECT_EQ(0, combobox_->selected_index()); } TEST_F(ComboboxTest, ListenerHandlesDelete) { TestComboboxModel model; - TestCombobox* combobox = new TestCombobox(&model); // Deleted on change. - EvilListener evil_listener; - combobox->set_listener(&evil_listener); + + // |combobox| will be deleted on change. + TestCombobox* combobox = new TestCombobox(&model); + scoped_ptr<EvilListener> evil_listener(new EvilListener()); + combobox->set_listener(evil_listener.get()); + ASSERT_NO_FATAL_FAILURE(combobox->ExecuteCommand(2)); + EXPECT_TRUE(evil_listener->deleted()); + + // With STYLE_ACTION + // |combobox| will be deleted on change. + combobox = new TestCombobox(&model); + evil_listener.reset(new EvilListener()); + combobox->set_listener(evil_listener.get()); + combobox->SetStyle(Combobox::STYLE_ACTION); ASSERT_NO_FATAL_FAILURE(combobox->ExecuteCommand(2)); - EXPECT_TRUE(evil_listener.deleted()); + EXPECT_TRUE(evil_listener->deleted()); } TEST_F(ComboboxTest, Click) { @@ -462,24 +515,46 @@ TEST_F(ComboboxTest, Click) { test_api.SetMenuRunnerHandler(menu_runner_handler.Pass()); PerformClick(gfx::Point(combobox_->x() + 1, combobox_->y() + combobox_->height() / 2)); - EXPECT_FALSE(listener.on_combobox_text_button_clicked_called()); + EXPECT_FALSE(listener.on_perform_action_called()); EXPECT_TRUE(test_menu_runner_handler->executed()); } +TEST_F(ComboboxTest, ClickButDisabled) { + InitCombobox(); + + TestComboboxListener listener; + combobox_->set_listener(&listener); + + combobox_->Layout(); + combobox_->SetEnabled(false); + + // Click the left side, but nothing happens since the combobox is disabled. + TestMenuRunnerHandler* test_menu_runner_handler = new TestMenuRunnerHandler(); + scoped_ptr<MenuRunnerHandler> menu_runner_handler(test_menu_runner_handler); + test::MenuRunnerTestAPI test_api( + combobox_->dropdown_list_menu_runner_.get()); + test_api.SetMenuRunnerHandler(menu_runner_handler.Pass()); + PerformClick(gfx::Point(combobox_->x() + 1, + combobox_->y() + combobox_->height() / 2)); + EXPECT_FALSE(listener.on_perform_action_called()); + EXPECT_FALSE(test_menu_runner_handler->executed()); +} + TEST_F(ComboboxTest, NotifyOnClickWithReturnKey) { InitCombobox(); TestComboboxListener listener; combobox_->set_listener(&listener); - // With STYLE_SHOW_DROP_DOWN_ON_CLICK, the click event is ignored. + // With STYLE_NORMAL, the click event is ignored. SendKeyEvent(ui::VKEY_RETURN); - EXPECT_FALSE(listener.on_combobox_text_button_clicked_called()); + EXPECT_FALSE(listener.on_perform_action_called()); - // With STYLE_NOTIFY_ON_CLICK, the click event is notified. - combobox_->SetStyle(Combobox::STYLE_NOTIFY_ON_CLICK); + // With STYLE_ACTION, the click event is notified. + combobox_->SetStyle(Combobox::STYLE_ACTION); SendKeyEvent(ui::VKEY_RETURN); - EXPECT_TRUE(listener.on_combobox_text_button_clicked_called()); + EXPECT_TRUE(listener.on_perform_action_called()); + EXPECT_EQ(0, listener.perform_action_index()); } TEST_F(ComboboxTest, NotifyOnClickWithSpaceKey) { @@ -488,18 +563,19 @@ TEST_F(ComboboxTest, NotifyOnClickWithSpaceKey) { TestComboboxListener listener; combobox_->set_listener(&listener); - // With STYLE_SHOW_DROP_DOWN_ON_CLICK, the click event is ignored. + // With STYLE_NORMAL, the click event is ignored. SendKeyEvent(ui::VKEY_SPACE); - EXPECT_FALSE(listener.on_combobox_text_button_clicked_called()); + EXPECT_FALSE(listener.on_perform_action_called()); SendKeyEventWithType(ui::VKEY_SPACE, ui::ET_KEY_RELEASED); - EXPECT_FALSE(listener.on_combobox_text_button_clicked_called()); + EXPECT_FALSE(listener.on_perform_action_called()); - // With STYLE_NOTIFY_ON_CLICK, the click event is notified after releasing. - combobox_->SetStyle(Combobox::STYLE_NOTIFY_ON_CLICK); + // With STYLE_ACTION, the click event is notified after releasing. + combobox_->SetStyle(Combobox::STYLE_ACTION); SendKeyEvent(ui::VKEY_SPACE); - EXPECT_FALSE(listener.on_combobox_text_button_clicked_called()); + EXPECT_FALSE(listener.on_perform_action_called()); SendKeyEventWithType(ui::VKEY_SPACE, ui::ET_KEY_RELEASED); - EXPECT_TRUE(listener.on_combobox_text_button_clicked_called()); + EXPECT_TRUE(listener.on_perform_action_called()); + EXPECT_EQ(0, listener.perform_action_index()); } TEST_F(ComboboxTest, NotifyOnClickWithMouse) { @@ -508,7 +584,7 @@ TEST_F(ComboboxTest, NotifyOnClickWithMouse) { TestComboboxListener listener; combobox_->set_listener(&listener); - combobox_->SetStyle(Combobox::STYLE_NOTIFY_ON_CLICK); + combobox_->SetStyle(Combobox::STYLE_ACTION); combobox_->Layout(); // Click the right side (arrow button). The menu is shown. @@ -520,7 +596,7 @@ TEST_F(ComboboxTest, NotifyOnClickWithMouse) { PerformClick(gfx::Point(combobox_->x() + combobox_->width() - 1, combobox_->y() + combobox_->height() / 2)); - EXPECT_FALSE(listener.on_combobox_text_button_clicked_called()); + EXPECT_FALSE(listener.on_perform_action_called()); EXPECT_TRUE(test_menu_runner_handler->executed()); // Click the left side (text button). The click event is notified. @@ -531,8 +607,9 @@ TEST_F(ComboboxTest, NotifyOnClickWithMouse) { test_api->SetMenuRunnerHandler(menu_runner_handler.Pass()); PerformClick(gfx::Point(combobox_->x() + 1, combobox_->y() + combobox_->height() / 2)); - EXPECT_TRUE(listener.on_combobox_text_button_clicked_called()); + EXPECT_TRUE(listener.on_perform_action_called()); EXPECT_FALSE(test_menu_runner_handler->executed()); + EXPECT_EQ(0, listener.perform_action_index()); } TEST_F(ComboboxTest, ConsumingPressKeyEvents) { @@ -543,13 +620,74 @@ TEST_F(ComboboxTest, ConsumingPressKeyEvents) { EXPECT_FALSE(combobox_->OnKeyPressed( ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SPACE, 0, false))); - // When the combobox's style is STYLE_NOTIFY_ON_CLICK, pressing events of - // a space key or an enter key will be consumed. - combobox_->SetStyle(Combobox::STYLE_NOTIFY_ON_CLICK); + // When the combobox's style is STYLE_ACTION, pressing events of a space key + // or an enter key will be consumed. + combobox_->SetStyle(Combobox::STYLE_ACTION); EXPECT_TRUE(combobox_->OnKeyPressed( ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, 0, false))); EXPECT_TRUE(combobox_->OnKeyPressed( ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SPACE, 0, false))); } +TEST_F(ComboboxTest, ContentWidth) { + std::vector<std::string> values; + VectorComboboxModel model(&values); + TestCombobox combobox(&model); + + std::string long_item = "this is the long item"; + std::string short_item = "s"; + + values.resize(1); + values[0] = long_item; + combobox.ModelChanged(); + + const int long_item_width = combobox.content_size_.width(); + + values[0] = short_item; + combobox.ModelChanged(); + + const int short_item_width = combobox.content_size_.width(); + + values.resize(2); + values[0] = short_item; + values[1] = long_item; + combobox.ModelChanged(); + + // When the style is STYLE_NORMAL, the width will fit with the longest item. + combobox.SetStyle(Combobox::STYLE_NORMAL); + EXPECT_EQ(long_item_width, combobox.content_size_.width()); + + // When the style is STYLE_ACTION, the width will fit with the first items' + // width. + combobox.SetStyle(Combobox::STYLE_ACTION); + EXPECT_EQ(short_item_width, combobox.content_size_.width()); +} + +TEST_F(ComboboxTest, TypingPrefixNotifiesListener) { + InitCombobox(); + + TestComboboxListener listener; + combobox_->set_listener(&listener); + + // Type the first character of the second menu item ("JELLY"). + combobox_->GetTextInputClient()->InsertChar('J', ui::EF_NONE); + EXPECT_EQ(1, listener.actions_performed()); + EXPECT_EQ(1, listener.perform_action_index()); + + // Type the second character of "JELLY", item shouldn't change and + // OnPerformAction() shouldn't be re-called. + combobox_->GetTextInputClient()->InsertChar('E', ui::EF_NONE); + EXPECT_EQ(1, listener.actions_performed()); + EXPECT_EQ(1, listener.perform_action_index()); + + // Clears the typed text. + combobox_->OnBlur(); + + // Type the first character of "PEANUT BUTTER", which should change the + // selected index and perform an action. + combobox_->GetTextInputClient()->InsertChar('P', ui::EF_NONE); + EXPECT_EQ(2, listener.actions_performed()); + EXPECT_EQ(2, listener.perform_action_index()); +} + } // namespace views diff --git a/chromium/ui/views/controls/image_view.cc b/chromium/ui/views/controls/image_view.cc index 2bbf91cab9e..5b758f8e1c7 100644 --- a/chromium/ui/views/controls/image_view.cc +++ b/chromium/ui/views/controls/image_view.cc @@ -7,13 +7,25 @@ #include "base/logging.h" #include "base/strings/utf_string_conversions.h" #include "third_party/skia/include/core/SkPaint.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/gfx/canvas.h" #include "ui/gfx/insets.h" #include "ui/views/painter.h" namespace views { +namespace { + +// Returns the pixels for the bitmap in |image| at scale |image_scale|. +void* GetBitmapPixels(const gfx::ImageSkia& img, float image_scale) { + DCHECK_NE(0.0f, image_scale); + const SkBitmap& bitmap = img.GetRepresentation(image_scale).sk_bitmap(); + SkAutoLockPixels pixel_lock(bitmap); + return bitmap.getPixels(); +} + +} // namespace + ImageView::ImageView() : image_size_set_(false), horiz_alignment_(CENTER), @@ -58,7 +70,7 @@ void ImageView::SetImageSize(const gfx::Size& image_size) { PreferredSizeChanged(); } -bool ImageView::GetImageSize(gfx::Size* image_size) { +bool ImageView::GetImageSize(gfx::Size* image_size) const { DCHECK(image_size); if (image_size_set_) *image_size = image_size_; @@ -79,7 +91,7 @@ void ImageView::SetFocusPainter(scoped_ptr<Painter> focus_painter) { focus_painter_ = focus_painter.Pass(); } -gfx::Size ImageView::GetPreferredSize() { +gfx::Size ImageView::GetPreferredSize() const { gfx::Insets insets = GetInsets(); if (image_size_set_) { gfx::Size image_size; @@ -99,8 +111,7 @@ bool ImageView::IsImageEqual(const gfx::ImageSkia& img) const { // the backing store but also the pixels of the last image we painted. return image_.BackedBySameObjectAs(img) && last_paint_scale_ != 0.0f && - last_painted_bitmap_pixels_ == - img.GetRepresentation(last_paint_scale_).sk_bitmap().getPixels(); + last_painted_bitmap_pixels_ == GetBitmapPixels(img, last_paint_scale_); } gfx::Point ImageView::ComputeImageOrigin(const gfx::Size& image_size) const { @@ -150,8 +161,8 @@ void ImageView::OnPaint(gfx::Canvas* canvas) { Painter::PaintFocusPainter(this, canvas, focus_painter_.get()); } -void ImageView::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_GRAPHIC; +void ImageView::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_IMAGE; state->name = tooltip_text_; } @@ -177,15 +188,16 @@ ImageView::Alignment ImageView::GetVerticalAlignment() const { return vert_alignment_; } -void ImageView::SetTooltipText(const string16& tooltip) { +void ImageView::SetTooltipText(const base::string16& tooltip) { tooltip_text_ = tooltip; } -string16 ImageView::GetTooltipText() const { +base::string16 ImageView::GetTooltipText() const { return tooltip_text_; } -bool ImageView::GetTooltipText(const gfx::Point& p, string16* tooltip) const { +bool ImageView::GetTooltipText(const gfx::Point& p, + base::string16* tooltip) const { if (tooltip_text_.empty()) return false; @@ -193,11 +205,10 @@ bool ImageView::GetTooltipText(const gfx::Point& p, string16* tooltip) const { return true; } -bool ImageView::HitTestRect(const gfx::Rect& rect) const { - return interactive_ ? View::HitTestRect(rect) : false; +bool ImageView::CanProcessEventsWithinSubtree() const { + return interactive_; } - void ImageView::OnPaintImage(gfx::Canvas* canvas) { last_paint_scale_ = canvas->image_scale(); last_painted_bitmap_pixels_ = NULL; @@ -212,15 +223,14 @@ void ImageView::OnPaintImage(gfx::Canvas* canvas) { if (image_bounds.size() != gfx::Size(image_.width(), image_.height())) { // Resize case SkPaint paint; - paint.setFilterBitmap(true); + paint.setFilterLevel(SkPaint::kLow_FilterLevel); canvas->DrawImageInt(image_, 0, 0, image_.width(), image_.height(), image_bounds.x(), image_bounds.y(), image_bounds.width(), image_bounds.height(), true, paint); } else { canvas->DrawImageInt(image_, image_bounds.x(), image_bounds.y()); } - last_painted_bitmap_pixels_ = - image_.GetRepresentation(last_paint_scale_).sk_bitmap().getPixels(); + last_painted_bitmap_pixels_ = GetBitmapPixels(image_, last_paint_scale_); } } // namespace views diff --git a/chromium/ui/views/controls/image_view.h b/chromium/ui/views/controls/image_view.h index b0f4ce852ff..a05b106ecfa 100644 --- a/chromium/ui/views/controls/image_view.h +++ b/chromium/ui/views/controls/image_view.h @@ -55,7 +55,7 @@ class VIEWS_EXPORT ImageView : public View { // Return the preferred size for the receiving view. Returns false if the // preferred size is not defined, which means that the view uses the image // size. - bool GetImageSize(gfx::Size* image_size); + bool GetImageSize(gfx::Size* image_size) const; // Returns the actual bounds of the visible image inside the view. gfx::Rect GetImageBounds() const; @@ -72,22 +72,22 @@ class VIEWS_EXPORT ImageView : public View { Alignment GetVerticalAlignment() const; // Set / Get the tooltip text. - void SetTooltipText(const string16& tooltip); - string16 GetTooltipText() const; + void SetTooltipText(const base::string16& tooltip); + base::string16 GetTooltipText() const; void set_interactive(bool interactive) { interactive_ = interactive; } void SetFocusPainter(scoped_ptr<Painter> focus_painter); // Overriden from View: - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual void OnFocus() OVERRIDE; virtual void OnBlur() OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; virtual bool GetTooltipText(const gfx::Point& p, - string16* tooltip) const OVERRIDE; - virtual bool HitTestRect(const gfx::Rect& rect) const OVERRIDE; + base::string16* tooltip) const OVERRIDE; + virtual bool CanProcessEventsWithinSubtree() const OVERRIDE; private: void OnPaintImage(gfx::Canvas* canvas); @@ -117,7 +117,7 @@ class VIEWS_EXPORT ImageView : public View { Alignment vert_alignment_; // The current tooltip text. - string16 tooltip_text_; + base::string16 tooltip_text_; // A flag controlling hit test handling for interactivity. bool interactive_; diff --git a/chromium/ui/views/controls/label.cc b/chromium/ui/views/controls/label.cc index 7d4bc7e8470..648a0e660bc 100644 --- a/chromium/ui/views/controls/label.cc +++ b/chromium/ui/views/controls/label.cc @@ -14,27 +14,21 @@ #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" #include "ui/gfx/color_utils.h" #include "ui/gfx/insets.h" -#include "ui/gfx/shadow_value.h" #include "ui/gfx/text_elider.h" #include "ui/gfx/text_utils.h" +#include "ui/gfx/utf16_indexing.h" #include "ui/native_theme/native_theme.h" #include "ui/views/background.h" namespace { -// The padding for the focus border when rendering focused text. -const int kFocusBorderPadding = 1; const int kCachedSizeLimit = 10; - -gfx::FontList GetDefaultFontList() { - return ui::ResourceBundle::GetSharedInstance().GetFontList( - ui::ResourceBundle::BaseFont); -} +const base::char16 kPasswordReplacementChar = '*'; } // namespace @@ -42,23 +36,20 @@ namespace views { // static const char Label::kViewClassName[] = "Label"; +const int Label::kFocusBorderPadding = 1; Label::Label() { - Init(string16(), GetDefaultFontList()); + Init(base::string16(), gfx::FontList()); } -Label::Label(const string16& text) { - Init(text, GetDefaultFontList()); +Label::Label(const base::string16& text) { + Init(text, gfx::FontList()); } -Label::Label(const string16& text, const gfx::FontList& font_list) { +Label::Label(const base::string16& text, const gfx::FontList& font_list) { Init(text, font_list); } -Label::Label(const string16& text, const gfx::Font& font) { - Init(text, gfx::FontList(font)); -} - Label::~Label() { } @@ -69,18 +60,22 @@ void Label::SetFontList(const gfx::FontList& font_list) { SchedulePaint(); } -const gfx::Font& Label::font() const { - return font_list_.GetPrimaryFont(); -} - -void Label::SetFont(const gfx::Font& font) { - SetFontList(gfx::FontList(font)); +void Label::SetText(const base::string16& text) { + if (text != text_) + SetTextInternal(text); } -void Label::SetText(const string16& text) { - if (text == text_) - return; +void Label::SetTextInternal(const base::string16& text) { text_ = text; + + if (is_obscured_) { + size_t obscured_text_length = + static_cast<size_t>(gfx::UTF16IndexToOffset(text_, 0, text_.length())); + layout_text_.assign(obscured_text_length, kPasswordReplacementChar); + } else { + layout_text_ = text_; + } + ResetCachedSize(); PreferredSizeChanged(); SchedulePaint(); @@ -109,26 +104,10 @@ void Label::SetBackgroundColor(SkColor color) { RecalculateColors(); } -void Label::SetShadowColors(SkColor enabled_color, SkColor disabled_color) { - enabled_shadow_color_ = enabled_color; - disabled_shadow_color_ = disabled_color; - has_shadow_ = true; -} - -void Label::SetShadowOffset(int x, int y) { - shadow_offset_.SetPoint(x, y); -} - -void Label::ClearEmbellishing() { - has_shadow_ = false; -} - void Label::SetHorizontalAlignment(gfx::HorizontalAlignment alignment) { - // If the View's UI layout is right-to-left and directionality_mode_ is - // USE_UI_DIRECTIONALITY, we need to flip the alignment so that the alignment - // settings take into account the text directionality. - if (base::i18n::IsRTL() && (directionality_mode_ == USE_UI_DIRECTIONALITY) && - (alignment != gfx::ALIGN_CENTER)) { + // If the UI layout is right-to-left, flip the alignment direction. + if (base::i18n::IsRTL() && + (alignment == gfx::ALIGN_LEFT || alignment == gfx::ALIGN_RIGHT)) { alignment = (alignment == gfx::ALIGN_LEFT) ? gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT; } @@ -138,6 +117,15 @@ void Label::SetHorizontalAlignment(gfx::HorizontalAlignment alignment) { } } +gfx::HorizontalAlignment Label::GetHorizontalAlignment() const { + if (horizontal_alignment_ != gfx::ALIGN_TO_HEAD) + return horizontal_alignment_; + + const base::i18n::TextDirection dir = + base::i18n::GetFirstStrongCharacterDirection(layout_text()); + return dir == base::i18n::RIGHT_TO_LEFT ? gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT; +} + void Label::SetLineHeight(int height) { if (height != line_height_) { line_height_ = height; @@ -148,7 +136,8 @@ void Label::SetLineHeight(int height) { } void Label::SetMultiLine(bool multi_line) { - DCHECK(!multi_line || elide_behavior_ != ELIDE_IN_MIDDLE); + DCHECK(!multi_line || (elide_behavior_ == gfx::ELIDE_TAIL || + elide_behavior_ == gfx::TRUNCATE)); if (multi_line != is_multi_line_) { is_multi_line_ = multi_line; ResetCachedSize(); @@ -157,6 +146,13 @@ void Label::SetMultiLine(bool multi_line) { } } +void Label::SetObscured(bool obscured) { + if (obscured != is_obscured_) { + is_obscured_ = obscured; + SetTextInternal(text_); + } +} + void Label::SetAllowCharacterBreak(bool allow_character_break) { if (allow_character_break != allow_character_break_) { allow_character_break_ = allow_character_break; @@ -166,8 +162,9 @@ void Label::SetAllowCharacterBreak(bool allow_character_break) { } } -void Label::SetElideBehavior(ElideBehavior elide_behavior) { - DCHECK(elide_behavior != ELIDE_IN_MIDDLE || !is_multi_line_); +void Label::SetElideBehavior(gfx::ElideBehavior elide_behavior) { + DCHECK(!is_multi_line_ || (elide_behavior_ == gfx::ELIDE_TAIL || + elide_behavior_ == gfx::TRUNCATE)); if (elide_behavior != elide_behavior_) { elide_behavior_ = elide_behavior; ResetCachedSize(); @@ -176,18 +173,18 @@ void Label::SetElideBehavior(ElideBehavior elide_behavior) { } } -void Label::SetTooltipText(const string16& tooltip_text) { +void Label::SetTooltipText(const base::string16& tooltip_text) { tooltip_text_ = tooltip_text; } void Label::SizeToFit(int max_width) { DCHECK(is_multi_line_); - std::vector<string16> lines; - base::SplitString(text_, '\n', &lines); + std::vector<base::string16> lines; + base::SplitString(layout_text(), '\n', &lines); int label_width = 0; - for (std::vector<string16>::const_iterator iter = lines.begin(); + for (std::vector<base::string16>::const_iterator iter = lines.begin(); iter != lines.end(); ++iter) { label_width = std::max(label_width, gfx::GetStringWidth(*iter, font_list_)); } @@ -201,17 +198,9 @@ void Label::SizeToFit(int max_width) { SizeToPreferredSize(); } -void Label::SetHasFocusBorder(bool has_focus_border) { - has_focus_border_ = has_focus_border; - if (is_multi_line_) { - ResetCachedSize(); - PreferredSizeChanged(); - } -} - gfx::Insets Label::GetInsets() const { gfx::Insets insets = View::GetInsets(); - if (focusable() || has_focus_border_) { + if (focusable()) { insets += gfx::Insets(kFocusBorderPadding, kFocusBorderPadding, kFocusBorderPadding, kFocusBorderPadding); } @@ -222,7 +211,7 @@ int Label::GetBaseline() const { return GetInsets().top() + font_list_.GetBaseline(); } -gfx::Size Label::GetPreferredSize() { +gfx::Size Label::GetPreferredSize() const { // Return a size of (0, 0) if the label is not visible and if the // collapse_when_hidden_ flag is set. // TODO(munjal): This logic probably belongs to the View class. But for now, @@ -231,13 +220,27 @@ gfx::Size Label::GetPreferredSize() { if (!visible() && collapse_when_hidden_) return gfx::Size(); - gfx::Size prefsize(GetTextSize()); + gfx::Size size(GetTextSize()); + gfx::Insets insets = GetInsets(); + size.Enlarge(insets.width(), insets.height()); + return size; +} + +gfx::Size Label::GetMinimumSize() const { + gfx::Size text_size(GetTextSize()); + if ((!visible() && collapse_when_hidden_) || text_size.IsEmpty()) + return gfx::Size(); + + gfx::Size size(gfx::GetStringWidth(base::string16(gfx::kEllipsisUTF16), + font_list_), + font_list_.GetHeight()); + size.SetToMin(text_size); // The actual text may be shorter than an ellipsis. gfx::Insets insets = GetInsets(); - prefsize.Enlarge(insets.width(), insets.height()); - return prefsize; + size.Enlarge(insets.width(), insets.height()); + return size; } -int Label::GetHeightForWidth(int w) { +int Label::GetHeightForWidth(int w) const { if (!is_multi_line_) return View::GetHeightForWidth(w); @@ -253,7 +256,8 @@ int Label::GetHeightForWidth(int w) { int h = font_list_.GetHeight(); const int flags = ComputeDrawStringFlags(); - gfx::Canvas::SizeStringInt(text_, font_list_, &w, &h, line_height_, flags); + gfx::Canvas::SizeStringInt( + layout_text(), font_list_, &w, &h, line_height_, flags); cached_heights_[cached_heights_cursor_] = gfx::Size(cache_width, h); cached_heights_cursor_ = (cached_heights_cursor_ + 1) % kCachedSizeLimit; return h + GetInsets().height(); @@ -278,11 +282,12 @@ View* Label::GetTooltipHandlerForPoint(const gfx::Point& point) { return this; } -bool Label::HitTestRect(const gfx::Rect& rect) const { +bool Label::CanProcessEventsWithinSubtree() const { + // Send events to the parent view for handling. return false; } -bool Label::GetTooltipText(const gfx::Point& p, string16* tooltip) const { +bool Label::GetTooltipText(const gfx::Point& p, base::string16* tooltip) const { DCHECK(tooltip); // If a tooltip has been explicitly set, use it. @@ -293,30 +298,30 @@ bool Label::GetTooltipText(const gfx::Point& p, string16* tooltip) const { // Show the full text if the text does not fit. if (ShouldShowDefaultTooltip()) { - *tooltip = text_; + *tooltip = layout_text(); return true; } return false; } -void Label::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_STATICTEXT; - state->state = ui::AccessibilityTypes::STATE_READONLY; - state->name = text_; +void Label::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_STATIC_TEXT; + state->AddStateFlag(ui::AX_STATE_READ_ONLY); + state->name = layout_text(); } void Label::PaintText(gfx::Canvas* canvas, - const string16& text, + const base::string16& text, const gfx::Rect& text_bounds, int flags) { - gfx::ShadowValues shadows; - if (has_shadow_) - shadows.push_back(gfx::ShadowValue(shadow_offset_, 0, - enabled() ? enabled_shadow_color_ : disabled_shadow_color_)); - canvas->DrawStringRectWithShadows(text, font_list_, - enabled() ? actual_enabled_color_ : actual_disabled_color_, - text_bounds, line_height_, flags, shadows); + SkColor color = enabled() ? actual_enabled_color_ : actual_disabled_color_; + if (elide_behavior_ == gfx::FADE_TAIL) { + canvas->DrawFadedString(text, font_list_, color, text_bounds, flags); + } else { + canvas->DrawStringRectWithShadows(text, font_list_, color, text_bounds, + line_height_, flags, shadows_); + } if (HasFocus()) { gfx::Rect focus_bounds = text_bounds; @@ -339,8 +344,11 @@ gfx::Size Label::GetTextSize() const { int flags = ComputeDrawStringFlags(); if (!is_multi_line_) flags |= gfx::Canvas::NO_ELLIPSIS; - gfx::Canvas::SizeStringInt(text_, font_list_, &w, &h, line_height_, flags); + gfx::Canvas::SizeStringInt( + layout_text(), font_list_, &w, &h, line_height_, flags); text_size_.SetSize(w, h); + const gfx::Insets shadow_margin = -gfx::ShadowValue::GetMargin(shadows_); + text_size_.Enlarge(shadow_margin.width(), shadow_margin.height()); text_size_valid_ = true; } @@ -358,7 +366,7 @@ void Label::OnPaint(gfx::Canvas* canvas) { // interfere with that. OnPaintBorder(canvas); - string16 paint_text; + base::string16 paint_text; gfx::Rect text_bounds; int flags = 0; CalculateDrawStringParams(&paint_text, &text_bounds, &flags); @@ -369,23 +377,20 @@ void Label::OnNativeThemeChanged(const ui::NativeTheme* theme) { UpdateColorsFromTheme(theme); } -void Label::Init(const string16& text, const gfx::FontList& font_list) { +void Label::Init(const base::string16& text, const gfx::FontList& font_list) { font_list_ = font_list; enabled_color_set_ = disabled_color_set_ = background_color_set_ = false; + subpixel_rendering_enabled_ = true; auto_color_readability_ = true; UpdateColorsFromTheme(ui::NativeTheme::instance()); horizontal_alignment_ = gfx::ALIGN_CENTER; line_height_ = 0; is_multi_line_ = false; + is_obscured_ = false; allow_character_break_ = false; - elide_behavior_ = ELIDE_AT_END; + elide_behavior_ = gfx::ELIDE_TAIL; collapse_when_hidden_ = false; - directionality_mode_ = USE_UI_DIRECTIONALITY; - has_focus_border_ = false; - enabled_shadow_color_ = 0; - disabled_shadow_color_ = 0; - shadow_offset_.SetPoint(1, 1); - has_shadow_ = false; + directionality_mode_ = gfx::DIRECTIONALITY_FROM_UI; cached_heights_.resize(kCachedSizeLimit); ResetCachedSize(); @@ -404,52 +409,50 @@ void Label::RecalculateColors() { } gfx::Rect Label::GetTextBounds() const { - gfx::Rect available_rect(GetAvailableRect()); + gfx::Rect available(GetAvailableRect()); gfx::Size text_size(GetTextSize()); - text_size.set_width(std::min(available_rect.width(), text_size.width())); - - gfx::Insets insets = GetInsets(); - gfx::Point text_origin(insets.left(), insets.top()); - switch (horizontal_alignment_) { + text_size.set_width(std::min(available.width(), text_size.width())); + gfx::Point origin(GetInsets().left(), GetInsets().top()); + switch (GetHorizontalAlignment()) { case gfx::ALIGN_LEFT: break; case gfx::ALIGN_CENTER: - // We put any extra margin pixel on the left rather than the right. We - // used to do this because measurement on Windows used - // GetTextExtentPoint32(), which could report a value one too large on the - // right; we now use DrawText(), and who knows if it can also do this. - text_origin.Offset((available_rect.width() + 1 - text_size.width()) / 2, - 0); + // Put any extra margin pixel on the left to match the legacy behavior + // from the use of GetTextExtentPoint32() on Windows. + origin.Offset((available.width() + 1 - text_size.width()) / 2, 0); break; case gfx::ALIGN_RIGHT: - text_origin.set_x(available_rect.right() - text_size.width()); + origin.set_x(available.right() - text_size.width()); break; default: NOTREACHED(); break; } - text_origin.Offset(0, - std::max(0, (available_rect.height() - text_size.height())) / 2); - return gfx::Rect(text_origin, text_size); + origin.Offset(0, std::max(0, (available.height() - text_size.height())) / 2); + return gfx::Rect(origin, text_size); } int Label::ComputeDrawStringFlags() const { int flags = 0; // We can't use subpixel rendering if the background is non-opaque. - if (SkColorGetA(background_color_) != 0xFF) + if (SkColorGetA(background_color_) != 0xFF || !subpixel_rendering_enabled_) flags |= gfx::Canvas::NO_SUBPIXEL_RENDERING; - if (directionality_mode_ == AUTO_DETECT_DIRECTIONALITY) { + if (directionality_mode_ == gfx::DIRECTIONALITY_FORCE_LTR) { + flags |= gfx::Canvas::FORCE_LTR_DIRECTIONALITY; + } else if (directionality_mode_ == gfx::DIRECTIONALITY_FORCE_RTL) { + flags |= gfx::Canvas::FORCE_RTL_DIRECTIONALITY; + } else if (directionality_mode_ == gfx::DIRECTIONALITY_FROM_TEXT) { base::i18n::TextDirection direction = - base::i18n::GetFirstStrongCharacterDirection(text_); + base::i18n::GetFirstStrongCharacterDirection(layout_text()); if (direction == base::i18n::RIGHT_TO_LEFT) flags |= gfx::Canvas::FORCE_RTL_DIRECTIONALITY; else flags |= gfx::Canvas::FORCE_LTR_DIRECTIONALITY; } - switch (horizontal_alignment_) { + switch (GetHorizontalAlignment()) { case gfx::ALIGN_LEFT: flags |= gfx::Canvas::TEXT_ALIGN_LEFT; break; @@ -459,6 +462,9 @@ int Label::ComputeDrawStringFlags() const { case gfx::ALIGN_RIGHT: flags |= gfx::Canvas::TEXT_ALIGN_RIGHT; break; + default: + NOTREACHED(); + break; } if (!is_multi_line_) @@ -485,31 +491,25 @@ gfx::Rect Label::GetAvailableRect() const { return bounds; } -void Label::CalculateDrawStringParams(string16* paint_text, +void Label::CalculateDrawStringParams(base::string16* paint_text, gfx::Rect* text_bounds, int* flags) const { DCHECK(paint_text && text_bounds && flags); - // TODO(msw): Use ElideRectangleText to support eliding multi-line text. Once - // this is done, we can set NO_ELLIPSIS unconditionally at the bottom. - if (is_multi_line_ || (elide_behavior_ == NO_ELIDE)) { - *paint_text = text_; - } else if (elide_behavior_ == ELIDE_IN_MIDDLE) { - *paint_text = gfx::ElideText(text_, font_list_, GetAvailableRect().width(), - gfx::ELIDE_IN_MIDDLE); - } else if (elide_behavior_ == ELIDE_AT_END) { - *paint_text = gfx::ElideText(text_, font_list_, GetAvailableRect().width(), - gfx::ELIDE_AT_END); + const bool forbid_ellipsis = elide_behavior_ == gfx::TRUNCATE || + elide_behavior_ == gfx::FADE_TAIL; + if (is_multi_line_ || forbid_ellipsis) { + *paint_text = layout_text(); } else { - DCHECK_EQ(ELIDE_AS_EMAIL, elide_behavior_); - *paint_text = gfx::ElideEmail(text_, font_list_, - GetAvailableRect().width()); + *paint_text = gfx::ElideText(layout_text(), font_list_, + GetAvailableRect().width(), elide_behavior_); } *text_bounds = GetTextBounds(); *flags = ComputeDrawStringFlags(); - if (!is_multi_line_ || (elide_behavior_ == NO_ELIDE)) - *flags |= gfx::Canvas::NO_ELLIPSIS; + // TODO(msw): Elide multi-line text with ElideRectangleText instead. + if (!is_multi_line_ || forbid_ellipsis) + *flags |= gfx::Canvas::NO_ELLIPSIS; } void Label::UpdateColorsFromTheme(const ui::NativeTheme* theme) { @@ -536,8 +536,9 @@ void Label::ResetCachedSize() { } bool Label::ShouldShowDefaultTooltip() const { - return !is_multi_line_ && - gfx::GetStringWidth(text_, font_list_) > GetAvailableRect().width(); + return !is_multi_line_ && !is_obscured_ && + gfx::GetStringWidth(layout_text(), font_list_) > + GetAvailableRect().width(); } } // namespace views diff --git a/chromium/ui/views/controls/label.h b/chromium/ui/views/controls/label.h index 40534ff654f..f146b18d4af 100644 --- a/chromium/ui/views/controls/label.h +++ b/chromium/ui/views/controls/label.h @@ -13,61 +13,33 @@ #include "base/strings/string16.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/font_list.h" +#include "ui/gfx/shadow_value.h" #include "ui/gfx/text_constants.h" #include "ui/views/view.h" namespace views { -///////////////////////////////////////////////////////////////////////////// -// -// Label class -// -// A label is a view subclass that can display a string. -// -///////////////////////////////////////////////////////////////////////////// +// A view subclass that can display a string. class VIEWS_EXPORT Label : public View { public: // Internal class name. static const char kViewClassName[]; - // The following enum is used to indicate whether using the Chrome UI's - // directionality as the label's directionality, or auto-detecting the label's - // directionality. - // - // If the label text originates from the Chrome UI, we should use the Chrome - // UI's directionality as the label's directionality. - // - // If the text originates from a web page, its directionality is determined - // based on its first character with strong directionality, disregarding what - // directionality the Chrome UI is. - enum DirectionalityMode { - USE_UI_DIRECTIONALITY = 0, - AUTO_DETECT_DIRECTIONALITY - }; - - enum ElideBehavior { - NO_ELIDE, // Do not elide the label text; truncate as needed. - ELIDE_IN_MIDDLE, // Add ellipsis in the middle of the string as needed. - ELIDE_AT_END, // Add ellipsis at the end of the string as needed. - ELIDE_AS_EMAIL, // Elide while retaining username/domain chars as needed. - }; + // The padding for the focus border when rendering focused text. + static const int kFocusBorderPadding; Label(); - explicit Label(const string16& text); - Label(const string16& text, const gfx::FontList& font_list); - Label(const string16& text, const gfx::Font& font); // OBSOLETE + explicit Label(const base::string16& text); + Label(const base::string16& text, const gfx::FontList& font_list); virtual ~Label(); // Gets or sets the fonts used by this label. const gfx::FontList& font_list() const { return font_list_; } virtual void SetFontList(const gfx::FontList& font_list); - // Obsolete gfx::Font version. Should use gfx::FontList version instead. - const gfx::Font& font() const; // OBSOLETE - virtual void SetFont(const gfx::Font& font); // OBSOLETE // Get or set the label text. - const string16& text() const { return text_; } - void SetText(const string16& text); + const base::string16& text() const { return text_; } + virtual void SetText(const base::string16& text); // Enables or disables auto-color-readability (enabled by default). If this // is enabled, then calls to set any foreground or background color will @@ -87,37 +59,30 @@ class VIEWS_EXPORT Label : public View { void SetBackgroundColor(SkColor color); SkColor background_color() const { return background_color_; } - // Enables a drop shadow underneath the text. - void SetShadowColors(SkColor enabled_color, SkColor disabled_color); - - // Sets the drop shadow's offset from the text. - void SetShadowOffset(int x, int y); + // Set drop shadows underneath the text. + void set_shadows(const gfx::ShadowValues& shadows) { + shadows_ = shadows; + text_size_valid_ = false; + } + const gfx::ShadowValues& shadows() const { return shadows_; } - // Disables shadows. - void ClearEmbellishing(); + // Sets whether subpixel rendering is used; the default is true, but this + // feature also requires an opaque background color. + void set_subpixel_rendering_enabled(bool subpixel_rendering_enabled) { + subpixel_rendering_enabled_ = subpixel_rendering_enabled; + } - // Sets horizontal alignment. If the locale is RTL, and the directionality - // mode is USE_UI_DIRECTIONALITY, the alignment is flipped around. - // - // Caveat: for labels originating from a web page, the directionality mode - // should be reset to AUTO_DETECT_DIRECTIONALITY before the horizontal - // alignment is set. Otherwise, the label's alignment specified as a parameter - // will be flipped in RTL locales. + // Sets the horizontal alignment; the argument value is mirrored in RTL UI. void SetHorizontalAlignment(gfx::HorizontalAlignment alignment); + gfx::HorizontalAlignment GetHorizontalAlignment() const; - gfx::HorizontalAlignment horizontal_alignment() const { - return horizontal_alignment_; - } - - // Sets the directionality mode. The directionality mode is initialized to - // USE_UI_DIRECTIONALITY when the label is constructed. USE_UI_DIRECTIONALITY - // applies to every label that originates from the Chrome UI. However, if the - // label originates from a web page, its directionality is auto-detected. - void set_directionality_mode(DirectionalityMode mode) { + // Sets the directionality mode. The default value is DIRECTIONALITY_FROM_UI, + // which should be suitable for most text originating from UI string assets. + // Most text originating from web content should use DIRECTIONALITY_FROM_TEXT. + void set_directionality_mode(gfx::DirectionalityMode mode) { directionality_mode_ = mode; } - - DirectionalityMode directionality_mode() const { + gfx::DirectionalityMode directionality_mode() const { return directionality_mode_; } @@ -131,20 +96,27 @@ class VIEWS_EXPORT Label : public View { bool is_multi_line() const { return is_multi_line_; } void SetMultiLine(bool multi_line); + // Get or set if the label text should be obscured before rendering (e.g. + // should "Password!" display as "*********"); default is false. + bool is_obscured() const { return is_obscured_; } + void SetObscured(bool obscured); + + // Get the text as displayed to the user, respecting the 'obscured' flag. + const base::string16& layout_text() const { return layout_text_; } + // Sets whether the label text can be split on words. // Default is false. This only works when is_multi_line is true. void SetAllowCharacterBreak(bool allow_character_break); - // Sets whether the label text should be elided in the middle or end (if - // necessary). The default is to elide at the end. - // NOTE: Eliding in the middle is not supported for multi-line strings. - void SetElideBehavior(ElideBehavior elide_behavior); + // Sets the eliding or fading behavior, applied as necessary. The default is + // to elide at the end. Eliding is not well supported for multi-line labels. + void SetElideBehavior(gfx::ElideBehavior elide_behavior); // Sets the tooltip text. Default behavior for a label (single-line) is to // show the full text if it is wider than its bounds. Calling this overrides // the default behavior and lets you set a custom tooltip. To revert to // default behavior, call this with an empty string. - void SetTooltipText(const string16& tooltip_text); + void SetTooltipText(const base::string16& tooltip_text); // Resizes the label so its width is set to the width of the longest line and // its height deduced accordingly. @@ -160,33 +132,33 @@ class VIEWS_EXPORT Label : public View { void set_collapse_when_hidden(bool value) { collapse_when_hidden_ = value; } bool collapse_when_hidden() const { return collapse_when_hidden_; } - void SetHasFocusBorder(bool has_focus_border); - - // Overridden from View: + // View: virtual gfx::Insets GetInsets() const OVERRIDE; virtual int GetBaseline() const OVERRIDE; // Overridden to compute the size required to display this label. - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; + // Returns the width of an ellipsis if the label is non-empty, or 0 otherwise. + virtual gfx::Size GetMinimumSize() const OVERRIDE; // Returns the height necessary to display this label with the provided width. // This method is used to layout multi-line labels. It is equivalent to // GetPreferredSize().height() if the receiver is not multi-line. - virtual int GetHeightForWidth(int w) OVERRIDE; + virtual int GetHeightForWidth(int w) const OVERRIDE; virtual const char* GetClassName() const OVERRIDE; virtual View* GetTooltipHandlerForPoint(const gfx::Point& point) OVERRIDE; - virtual bool HitTestRect(const gfx::Rect& rect) const OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual bool CanProcessEventsWithinSubtree() const OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; // Gets the tooltip text for labels that are wider than their bounds, except // when the label is multiline, in which case it just returns false (no // tooltip). If a custom tooltip has been specified with SetTooltipText() // it is returned instead. virtual bool GetTooltipText(const gfx::Point& p, - string16* tooltip) const OVERRIDE; + base::string16* tooltip) const OVERRIDE; protected: // Called by Paint to paint the text. Override this to change how // text is painted. virtual void PaintText(gfx::Canvas* canvas, - const string16& text, + const base::string16& text, const gfx::Rect& text_bounds, int flags); @@ -207,12 +179,14 @@ class VIEWS_EXPORT Label : public View { FRIEND_TEST_ALL_PREFIXES(LabelTest, DrawMultiLineString); FRIEND_TEST_ALL_PREFIXES(LabelTest, DrawSingleLineStringInRTL); FRIEND_TEST_ALL_PREFIXES(LabelTest, DrawMultiLineStringInRTL); - FRIEND_TEST_ALL_PREFIXES(LabelTest, AutoDetectDirectionality); - - // Calls ComputeDrawStringFlags(). + FRIEND_TEST_ALL_PREFIXES(LabelTest, DirectionalityFromText); FRIEND_TEST_ALL_PREFIXES(LabelTest, DisableSubpixelRendering); - void Init(const string16& text, const gfx::FontList& font_list); + // Sets both |text_| and |layout_text_| to appropriate values, taking + // the label's 'obscured' status into account. + void SetTextInternal(const base::string16& text); + + void Init(const base::string16& text, const gfx::FontList& font_list); void RecalculateColors(); @@ -224,7 +198,7 @@ class VIEWS_EXPORT Label : public View { gfx::Rect GetAvailableRect() const; // Returns parameters to be used for the DrawString call. - void CalculateDrawStringParams(string16* paint_text, + void CalculateDrawStringParams(base::string16* paint_text, gfx::Rect* text_bounds, int* flags) const; @@ -237,7 +211,8 @@ class VIEWS_EXPORT Label : public View { bool ShouldShowDefaultTooltip() const; - string16 text_; + base::string16 text_; + base::string16 layout_text_; gfx::FontList font_list_; SkColor requested_enabled_color_; SkColor actual_enabled_color_; @@ -250,39 +225,27 @@ class VIEWS_EXPORT Label : public View { bool disabled_color_set_; bool background_color_set_; + bool subpixel_rendering_enabled_; bool auto_color_readability_; mutable gfx::Size text_size_; mutable bool text_size_valid_; int line_height_; bool is_multi_line_; + bool is_obscured_; bool allow_character_break_; - ElideBehavior elide_behavior_; + gfx::ElideBehavior elide_behavior_; gfx::HorizontalAlignment horizontal_alignment_; - string16 tooltip_text_; + base::string16 tooltip_text_; // Whether to collapse the label when it's not visible. bool collapse_when_hidden_; - // The following member variable is used to control whether the - // directionality is auto-detected based on first strong directionality - // character or is determined by chrome UI's locale. - DirectionalityMode directionality_mode_; - // When embedded in a larger control that is focusable, setting this flag - // allows this view to reserve space for a focus border that it otherwise - // might not have because it is not itself focusable. - bool has_focus_border_; - - // Colors for shadow. - SkColor enabled_shadow_color_; - SkColor disabled_shadow_color_; - - // Space between text and shadow. - gfx::Point shadow_offset_; - - // Should a shadow be drawn behind the text? - bool has_shadow_; + // Controls whether the directionality is auto-detected based on first strong + // directionality character or is determined by the application UI's locale. + gfx::DirectionalityMode directionality_mode_; + gfx::ShadowValues shadows_; // The cached heights to avoid recalculation in GetHeightForWidth(). - std::vector<gfx::Size> cached_heights_; - int cached_heights_cursor_; + mutable std::vector<gfx::Size> cached_heights_; + mutable int cached_heights_cursor_; DISALLOW_COPY_AND_ASSIGN(Label); }; diff --git a/chromium/ui/views/controls/label_unittest.cc b/chromium/ui/views/controls/label_unittest.cc index e2a37c3a167..8b16d0f6844 100644 --- a/chromium/ui/views/controls/label_unittest.cc +++ b/chromium/ui/views/controls/label_unittest.cc @@ -7,16 +7,25 @@ #include "base/i18n/rtl.h" #include "base/strings/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/canvas.h" #include "ui/views/border.h" +using base::ASCIIToUTF16; + namespace views { // All text sizing measurements (width and height) should be greater than this. const int kMinTextDimension = 4; +// A test utility function to set the application default text direction. +void SetRTL(bool rtl) { + // Override the current locale/direction. + base::i18n::SetICUDefaultLocale(rtl ? "he" : "en"); + EXPECT_EQ(rtl, base::i18n::IsRTL()); +} + TEST(LabelTest, FontPropertySymbol) { Label label; std::string font_name("symbol"); @@ -39,7 +48,7 @@ TEST(LabelTest, FontPropertyArial) { TEST(LabelTest, TextProperty) { Label label; - string16 test_text(ASCIIToUTF16("A random string.")); + base::string16 test_text(ASCIIToUTF16("A random string.")); label.SetText(test_text); EXPECT_EQ(test_text, label.text()); } @@ -53,38 +62,45 @@ TEST(LabelTest, ColorProperty) { } TEST(LabelTest, AlignmentProperty) { - Label label; - bool reverse_alignment = base::i18n::IsRTL(); + const bool was_rtl = base::i18n::IsRTL(); - label.SetHorizontalAlignment(gfx::ALIGN_RIGHT); - EXPECT_EQ(reverse_alignment ? gfx::ALIGN_LEFT : gfx::ALIGN_RIGHT, - label.horizontal_alignment()); - label.SetHorizontalAlignment(gfx::ALIGN_LEFT); - EXPECT_EQ(reverse_alignment ? gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT, - label.horizontal_alignment()); - label.SetHorizontalAlignment(gfx::ALIGN_CENTER); - EXPECT_EQ(gfx::ALIGN_CENTER, label.horizontal_alignment()); + Label label; + for (size_t i = 0; i < 2; ++i) { + // Toggle the application default text direction (to try each direction). + SetRTL(!base::i18n::IsRTL()); + bool reverse_alignment = base::i18n::IsRTL(); + + // The alignment should be flipped in RTL UI. + label.SetHorizontalAlignment(gfx::ALIGN_RIGHT); + EXPECT_EQ(reverse_alignment ? gfx::ALIGN_LEFT : gfx::ALIGN_RIGHT, + label.GetHorizontalAlignment()); + label.SetHorizontalAlignment(gfx::ALIGN_LEFT); + EXPECT_EQ(reverse_alignment ? gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT, + label.GetHorizontalAlignment()); + label.SetHorizontalAlignment(gfx::ALIGN_CENTER); + EXPECT_EQ(gfx::ALIGN_CENTER, label.GetHorizontalAlignment()); + + for (size_t j = 0; j < 2; ++j) { + label.SetHorizontalAlignment(gfx::ALIGN_TO_HEAD); + const bool rtl = j == 0; + label.SetText(rtl ? base::WideToUTF16(L"\x5d0") : ASCIIToUTF16("A")); + EXPECT_EQ(rtl ? gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT, + label.GetHorizontalAlignment()); + } + } - // The label's alignment should not be flipped if the directionality mode is - // AUTO_DETECT_DIRECTIONALITY. - label.set_directionality_mode(Label::AUTO_DETECT_DIRECTIONALITY); - label.SetHorizontalAlignment(gfx::ALIGN_RIGHT); - EXPECT_EQ(gfx::ALIGN_RIGHT, label.horizontal_alignment()); - label.SetHorizontalAlignment(gfx::ALIGN_LEFT); - EXPECT_EQ(gfx::ALIGN_LEFT, label.horizontal_alignment()); - label.SetHorizontalAlignment(gfx::ALIGN_CENTER); - EXPECT_EQ(gfx::ALIGN_CENTER, label.horizontal_alignment()); + EXPECT_EQ(was_rtl, base::i18n::IsRTL()); } TEST(LabelTest, DirectionalityModeProperty) { Label label; - EXPECT_EQ(Label::USE_UI_DIRECTIONALITY, label.directionality_mode()); + EXPECT_EQ(gfx::DIRECTIONALITY_FROM_UI, label.directionality_mode()); - label.set_directionality_mode(Label::AUTO_DETECT_DIRECTIONALITY); - EXPECT_EQ(Label::AUTO_DETECT_DIRECTIONALITY, label.directionality_mode()); + label.set_directionality_mode(gfx::DIRECTIONALITY_FROM_TEXT); + EXPECT_EQ(gfx::DIRECTIONALITY_FROM_TEXT, label.directionality_mode()); - label.set_directionality_mode(Label::USE_UI_DIRECTIONALITY); - EXPECT_EQ(Label::USE_UI_DIRECTIONALITY, label.directionality_mode()); + label.set_directionality_mode(gfx::DIRECTIONALITY_FROM_UI); + EXPECT_EQ(gfx::DIRECTIONALITY_FROM_UI, label.directionality_mode()); } TEST(LabelTest, MultiLineProperty) { @@ -96,24 +112,59 @@ TEST(LabelTest, MultiLineProperty) { EXPECT_FALSE(label.is_multi_line()); } -TEST(LabelTest, TooltipProperty) { +TEST(LabelTest, ObscuredProperty) { Label label; - string16 test_text(ASCIIToUTF16("My cool string.")); + base::string16 test_text(ASCIIToUTF16("Password!")); label.SetText(test_text); - string16 tooltip; + // Should be false by default... + EXPECT_FALSE(label.is_obscured()); + EXPECT_EQ(test_text, label.layout_text()); + EXPECT_EQ(test_text, label.text()); + + label.SetObscured(true); + EXPECT_TRUE(label.is_obscured()); + EXPECT_EQ(ASCIIToUTF16("*********"), label.layout_text()); + EXPECT_EQ(test_text, label.text()); + + label.SetText(test_text + test_text); + EXPECT_EQ(ASCIIToUTF16("******************"), label.layout_text()); + EXPECT_EQ(test_text + test_text, label.text()); + + label.SetObscured(false); + EXPECT_FALSE(label.is_obscured()); + EXPECT_EQ(test_text + test_text, label.layout_text()); + EXPECT_EQ(test_text + test_text, label.text()); +} + +TEST(LabelTest, ObscuredSurrogatePair) { + // 'MUSICAL SYMBOL G CLEF': represented in UTF-16 as two characters + // forming the surrogate pair 0x0001D11E. + Label label; + base::string16 test_text = base::UTF8ToUTF16("\xF0\x9D\x84\x9E"); + label.SetText(test_text); + + label.SetObscured(true); + EXPECT_EQ(ASCIIToUTF16("*"), label.layout_text()); + EXPECT_EQ(test_text, label.text()); +} + +TEST(LabelTest, TooltipProperty) { + Label label; + label.SetText(ASCIIToUTF16("My cool string.")); + + base::string16 tooltip; EXPECT_TRUE(label.GetTooltipText(gfx::Point(), &tooltip)); - EXPECT_EQ(test_text, tooltip); + EXPECT_EQ(label.text(), tooltip); - string16 tooltip_text(ASCIIToUTF16("The tooltip!")); + base::string16 tooltip_text(ASCIIToUTF16("The tooltip!")); label.SetTooltipText(tooltip_text); EXPECT_TRUE(label.GetTooltipText(gfx::Point(), &tooltip)); EXPECT_EQ(tooltip_text, tooltip); - string16 empty_text; - label.SetTooltipText(empty_text); + label.SetTooltipText(base::string16()); EXPECT_TRUE(label.GetTooltipText(gfx::Point(), &tooltip)); - EXPECT_EQ(test_text, tooltip); + EXPECT_EQ(label.text(), tooltip); // Make the label big enough to hold the text // and expect there to be no tooltip. @@ -125,13 +176,21 @@ TEST(LabelTest, TooltipProperty) { EXPECT_TRUE(label.GetTooltipText(gfx::Point(), &tooltip)); EXPECT_EQ(tooltip_text, tooltip); // Clear out the tooltip. - label.SetTooltipText(empty_text); + label.SetTooltipText(base::string16()); // Shrink the bounds and the tooltip should come back. label.SetBounds(0, 0, 1, 1); EXPECT_TRUE(label.GetTooltipText(gfx::Point(), &tooltip)); - // Make the label multiline and there is no tooltip again. + // Make the label obscured and there is no tooltip. + label.SetObscured(true); + EXPECT_FALSE(label.GetTooltipText(gfx::Point(), &tooltip)); + + // Obscuring the text shouldn't permanently clobber the tooltip. + label.SetObscured(false); + EXPECT_TRUE(label.GetTooltipText(gfx::Point(), &tooltip)); + + // Make the label multiline and there is no tooltip. label.SetMultiLine(true); EXPECT_FALSE(label.GetTooltipText(gfx::Point(), &tooltip)); @@ -140,25 +199,23 @@ TEST(LabelTest, TooltipProperty) { EXPECT_TRUE(label.GetTooltipText(gfx::Point(), &tooltip)); EXPECT_EQ(tooltip_text, tooltip); // Clear out the tooltip. - label.SetTooltipText(empty_text); + label.SetTooltipText(base::string16()); } TEST(LabelTest, Accessibility) { Label label; - string16 test_text(ASCIIToUTF16("My special text.")); - label.SetText(test_text); + label.SetText(ASCIIToUTF16("My special text.")); - ui::AccessibleViewState state; + ui::AXViewState state; label.GetAccessibleState(&state); - EXPECT_EQ(ui::AccessibilityTypes::ROLE_STATICTEXT, state.role); - EXPECT_EQ(test_text, state.name); - EXPECT_TRUE(ui::AccessibilityTypes::STATE_READONLY & state.state); + EXPECT_EQ(ui::AX_ROLE_STATIC_TEXT, state.role); + EXPECT_EQ(label.text(), state.name); + EXPECT_TRUE(state.HasStateFlag(ui::AX_STATE_READ_ONLY)); } TEST(LabelTest, SingleLineSizing) { Label label; - string16 test_text(ASCIIToUTF16("A not so random string in one line.")); - label.SetText(test_text); + label.SetText(ASCIIToUTF16("A not so random string in one line.")); // GetPreferredSize gfx::Size required_size = label.GetPreferredSize(); @@ -167,10 +224,8 @@ TEST(LabelTest, SingleLineSizing) { // Test everything with borders. gfx::Insets border(10, 20, 30, 40); - label.set_border(Border::CreateEmptyBorder(border.top(), - border.left(), - border.bottom(), - border.right())); + label.SetBorder(Border::CreateEmptyBorder( + border.top(), border.left(), border.bottom(), border.right())); // GetPreferredSize and borders. label.SetBounds(0, 0, 0, 0); @@ -183,28 +238,23 @@ TEST(LabelTest, SingleLineSizing) { TEST(LabelTest, MultilineSmallAvailableWidthSizing) { Label label; - string16 test_text(ASCIIToUTF16("Too Wide.")); - label.SetMultiLine(true); label.SetAllowCharacterBreak(true); - label.SetElideBehavior(Label::ELIDE_AT_END); - label.SetText(test_text); + label.SetText(ASCIIToUTF16("Too Wide.")); // Check that Label can be laid out at a variety of small sizes, // splitting the words into up to one character per line if necessary. // Incorrect word splitting may cause infinite loops in text layout. gfx::Size required_size = label.GetPreferredSize(); - for (int i = 1; i < required_size.width(); ++i) { + for (int i = 1; i < required_size.width(); ++i) EXPECT_GT(label.GetHeightForWidth(i), 0); - } } TEST(LabelTest, MultiLineSizing) { Label label; label.SetFocusable(false); - string16 test_text( + label.SetText( ASCIIToUTF16("A random string\nwith multiple lines\nand returns!")); - label.SetText(test_text); label.SetMultiLine(true); // GetPreferredSize @@ -248,10 +298,8 @@ TEST(LabelTest, MultiLineSizing) { // Test everything with borders. gfx::Insets border(10, 20, 30, 40); - label.set_border(Border::CreateEmptyBorder(border.top(), - border.left(), - border.bottom(), - border.right())); + label.SetBorder(Border::CreateEmptyBorder( + border.top(), border.left(), border.bottom(), border.right())); // SizeToFit and borders. label.SizeToFit(0); @@ -283,37 +331,23 @@ TEST(LabelTest, MultiLineSizing) { required_size.width() + border.width()); } -TEST(LabelTest, AutoDetectDirectionality) { +TEST(LabelTest, DirectionalityFromText) { Label label; - label.set_directionality_mode(Label::AUTO_DETECT_DIRECTIONALITY); + label.set_directionality_mode(gfx::DIRECTIONALITY_FROM_TEXT); + label.SetBounds(0, 0, 1000, 1000); + base::string16 paint_text; + gfx::Rect text_bounds; + int flags = -1; // Test text starts with RTL character. - string16 test_text(WideToUTF16(L" \x5d0\x5d1\x5d2 abc")); - label.SetText(test_text); - gfx::Size required_size(label.GetPreferredSize()); - gfx::Size extra(22, 8); - label.SetBounds(0, - 0, - required_size.width() + extra.width(), - required_size.height() + extra.height()); - - string16 paint_text; - gfx::Rect text_bounds; - int flags; + label.SetText(base::WideToUTF16(L" \x5d0\x5d1\x5d2 abc")); label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); EXPECT_EQ(gfx::Canvas::FORCE_RTL_DIRECTIONALITY, flags & (gfx::Canvas::FORCE_RTL_DIRECTIONALITY | gfx::Canvas::FORCE_LTR_DIRECTIONALITY)); // Test text starts with LTR character. - test_text = (WideToUTF16(L"ltr \x5d0\x5d1\x5d2 abc")); - label.SetText(test_text); - required_size = label.GetPreferredSize(); - label.SetBounds(0, - 0, - required_size.width() + extra.width(), - required_size.height() + extra.height()); - + label.SetText(base::WideToUTF16(L"ltr \x5d0\x5d1\x5d2 abc")); label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); EXPECT_EQ(gfx::Canvas::FORCE_LTR_DIRECTIONALITY, flags & (gfx::Canvas::FORCE_RTL_DIRECTIONALITY | @@ -323,28 +357,23 @@ TEST(LabelTest, AutoDetectDirectionality) { TEST(LabelTest, DrawSingleLineString) { Label label; label.SetFocusable(false); + // Force a directionality to simplify alignment value testing. + label.set_directionality_mode(gfx::DIRECTIONALITY_FORCE_LTR); - // Turn off mirroring so that we don't need to figure out if - // align right really means align left. - label.set_directionality_mode(Label::AUTO_DETECT_DIRECTIONALITY); - - string16 test_text(ASCIIToUTF16("Here's a string with no returns.")); - label.SetText(test_text); + label.SetText(ASCIIToUTF16("Here's a string with no returns.")); gfx::Size required_size(label.GetPreferredSize()); gfx::Size extra(22, 8); - label.SetBounds(0, - 0, - required_size.width() + extra.width(), + label.SetBounds(0, 0, required_size.width() + extra.width(), required_size.height() + extra.height()); // Do some basic verifications for all three alignments. - string16 paint_text; + base::string16 paint_text; gfx::Rect text_bounds; - int flags; + int flags = -1; // Centered text. label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); // The text should be centered horizontally and vertically. EXPECT_EQ(extra.width() / 2, text_bounds.x()); EXPECT_EQ(extra.height() / 2 , text_bounds.y()); @@ -360,7 +389,7 @@ TEST(LabelTest, DrawSingleLineString) { paint_text.clear(); text_bounds.SetRect(0, 0, 0, 0); label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); // The text should be left aligned horizontally and centered vertically. EXPECT_EQ(0, text_bounds.x()); EXPECT_EQ(extra.height() / 2 , text_bounds.y()); @@ -376,7 +405,7 @@ TEST(LabelTest, DrawSingleLineString) { paint_text.clear(); text_bounds.SetRect(0, 0, 0, 0); label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); // The text should be right aligned horizontally and centered vertically. EXPECT_EQ(extra.width(), text_bounds.x()); EXPECT_EQ(extra.height() / 2 , text_bounds.y()); @@ -389,19 +418,15 @@ TEST(LabelTest, DrawSingleLineString) { // Test single line drawing with a border. gfx::Insets border(39, 34, 8, 96); - label.set_border(Border::CreateEmptyBorder(border.top(), - border.left(), - border.bottom(), - border.right())); + label.SetBorder(Border::CreateEmptyBorder( + border.top(), border.left(), border.bottom(), border.right())); gfx::Size required_size_with_border(label.GetPreferredSize()); EXPECT_EQ(required_size.width() + border.width(), required_size_with_border.width()); EXPECT_EQ(required_size.height() + border.height(), required_size_with_border.height()); - label.SetBounds(0, - 0, - required_size_with_border.width() + extra.width(), + label.SetBounds(0, 0, required_size_with_border.width() + extra.width(), required_size_with_border.height() + extra.height()); // Centered text with border. @@ -409,7 +434,7 @@ TEST(LabelTest, DrawSingleLineString) { paint_text.clear(); text_bounds.SetRect(0, 0, 0, 0); label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); // The text should be centered horizontally and vertically within the border. EXPECT_EQ(border.left() + extra.width() / 2, text_bounds.x()); EXPECT_EQ(border.top() + extra.height() / 2 , text_bounds.y()); @@ -425,7 +450,7 @@ TEST(LabelTest, DrawSingleLineString) { paint_text.clear(); text_bounds.SetRect(0, 0, 0, 0); label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); // The text should be left aligned horizontally and centered vertically. EXPECT_EQ(border.left(), text_bounds.x()); EXPECT_EQ(border.top() + extra.height() / 2 , text_bounds.y()); @@ -441,7 +466,7 @@ TEST(LabelTest, DrawSingleLineString) { paint_text.clear(); text_bounds.SetRect(0, 0, 0, 0); label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); // The text should be right aligned horizontally and centered vertically. EXPECT_EQ(border.left() + extra.width(), text_bounds.x()); EXPECT_EQ(border.top() + extra.height() / 2 , text_bounds.y()); @@ -453,33 +478,29 @@ TEST(LabelTest, DrawSingleLineString) { gfx::Canvas::TEXT_ALIGN_RIGHT)); } -// On Linux the underlying pango routines require a max height in order to -// ellide multiline text. So until that can be resolved, we set all -// multiline lables to not ellide in Linux only. +// Pango needs a max height to elide multiline text; that is not supported here. TEST(LabelTest, DrawMultiLineString) { Label label; label.SetFocusable(false); + // Force a directionality to simplify alignment value testing. + label.set_directionality_mode(gfx::DIRECTIONALITY_FORCE_LTR); + // Set a background color to prevent gfx::Canvas::NO_SUBPIXEL_RENDERING flags. + label.SetBackgroundColor(SK_ColorWHITE); - // Turn off mirroring so that we don't need to figure out if - // align right really means align left. - label.set_directionality_mode(Label::AUTO_DETECT_DIRECTIONALITY); - - string16 test_text(ASCIIToUTF16("Another string\nwith returns\n\n!")); - label.SetText(test_text); + label.SetText(ASCIIToUTF16("Another string\nwith returns\n\n!")); label.SetMultiLine(true); label.SizeToFit(0); gfx::Size extra(50, 10); - label.SetBounds(label.x(), - label.y(), + label.SetBounds(label.x(), label.y(), label.width() + extra.width(), label.height() + extra.height()); // Do some basic verifications for all three alignments. - string16 paint_text; + base::string16 paint_text; gfx::Rect text_bounds; - int flags; + int flags = -1; label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); EXPECT_EQ(extra.width() / 2, text_bounds.x()); EXPECT_EQ(extra.height() / 2, text_bounds.y()); EXPECT_GT(text_bounds.width(), kMinTextDimension); @@ -487,18 +508,17 @@ TEST(LabelTest, DrawMultiLineString) { int expected_flags = gfx::Canvas::MULTI_LINE | gfx::Canvas::TEXT_ALIGN_CENTER | gfx::Canvas::FORCE_LTR_DIRECTIONALITY; -#if defined(OS_WIN) - EXPECT_EQ(expected_flags, flags); -#else - EXPECT_EQ(expected_flags | gfx::Canvas::NO_ELLIPSIS, flags); +#if !defined(OS_WIN) + expected_flags |= gfx::Canvas::NO_ELLIPSIS; #endif + EXPECT_EQ(expected_flags, expected_flags); gfx::Rect center_bounds(text_bounds); label.SetHorizontalAlignment(gfx::ALIGN_LEFT); paint_text.clear(); text_bounds.SetRect(0, 0, 0, 0); label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); EXPECT_EQ(0, text_bounds.x()); EXPECT_EQ(extra.height() / 2, text_bounds.y()); EXPECT_GT(text_bounds.width(), kMinTextDimension); @@ -506,17 +526,16 @@ TEST(LabelTest, DrawMultiLineString) { expected_flags = gfx::Canvas::MULTI_LINE | gfx::Canvas::TEXT_ALIGN_LEFT | gfx::Canvas::FORCE_LTR_DIRECTIONALITY; -#if defined(OS_WIN) - EXPECT_EQ(expected_flags, flags); -#else - EXPECT_EQ(expected_flags | gfx::Canvas::NO_ELLIPSIS, flags); +#if !defined(OS_WIN) + expected_flags |= gfx::Canvas::NO_ELLIPSIS; #endif + EXPECT_EQ(expected_flags, expected_flags); label.SetHorizontalAlignment(gfx::ALIGN_RIGHT); paint_text.clear(); text_bounds.SetRect(0, 0, 0, 0); label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); EXPECT_EQ(extra.width(), text_bounds.x()); EXPECT_EQ(extra.height() / 2, text_bounds.y()); EXPECT_GT(text_bounds.width(), kMinTextDimension); @@ -524,21 +543,17 @@ TEST(LabelTest, DrawMultiLineString) { expected_flags = gfx::Canvas::MULTI_LINE | gfx::Canvas::TEXT_ALIGN_RIGHT | gfx::Canvas::FORCE_LTR_DIRECTIONALITY; -#if defined(OS_WIN) - EXPECT_EQ(expected_flags, flags); -#else - EXPECT_EQ(expected_flags | gfx::Canvas::NO_ELLIPSIS, flags); +#if !defined(OS_WIN) + expected_flags |= gfx::Canvas::NO_ELLIPSIS; #endif + EXPECT_EQ(expected_flags, expected_flags); // Test multiline drawing with a border. gfx::Insets border(19, 92, 23, 2); - label.set_border(Border::CreateEmptyBorder(border.top(), - border.left(), - border.bottom(), - border.right())); + label.SetBorder(Border::CreateEmptyBorder( + border.top(), border.left(), border.bottom(), border.right())); label.SizeToFit(0); - label.SetBounds(label.x(), - label.y(), + label.SetBounds(label.x(), label.y(), label.width() + extra.width(), label.height() + extra.height()); @@ -546,7 +561,7 @@ TEST(LabelTest, DrawMultiLineString) { paint_text.clear(); text_bounds.SetRect(0, 0, 0, 0); label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); EXPECT_EQ(border.left() + extra.width() / 2, text_bounds.x()); EXPECT_EQ(border.top() + extra.height() / 2, text_bounds.y()); EXPECT_EQ(center_bounds.width(), text_bounds.width()); @@ -554,17 +569,16 @@ TEST(LabelTest, DrawMultiLineString) { expected_flags = gfx::Canvas::MULTI_LINE | gfx::Canvas::TEXT_ALIGN_CENTER | gfx::Canvas::FORCE_LTR_DIRECTIONALITY; -#if defined(OS_WIN) - EXPECT_EQ(expected_flags, flags); -#else - EXPECT_EQ(expected_flags | gfx::Canvas::NO_ELLIPSIS, flags); +#if !defined(OS_WIN) + expected_flags |= gfx::Canvas::NO_ELLIPSIS; #endif + EXPECT_EQ(expected_flags, expected_flags); label.SetHorizontalAlignment(gfx::ALIGN_LEFT); paint_text.clear(); text_bounds.SetRect(0, 0, 0, 0); label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); EXPECT_EQ(border.left(), text_bounds.x()); EXPECT_EQ(border.top() + extra.height() / 2, text_bounds.y()); EXPECT_EQ(center_bounds.width(), text_bounds.width()); @@ -572,17 +586,16 @@ TEST(LabelTest, DrawMultiLineString) { expected_flags = gfx::Canvas::MULTI_LINE | gfx::Canvas::TEXT_ALIGN_LEFT | gfx::Canvas::FORCE_LTR_DIRECTIONALITY; -#if defined(OS_WIN) - EXPECT_EQ(expected_flags, flags); -#else - EXPECT_EQ(expected_flags | gfx::Canvas::NO_ELLIPSIS, flags); +#if !defined(OS_WIN) + expected_flags |= gfx::Canvas::NO_ELLIPSIS; #endif + EXPECT_EQ(expected_flags, expected_flags); label.SetHorizontalAlignment(gfx::ALIGN_RIGHT); paint_text.clear(); text_bounds.SetRect(0, 0, 0, 0); label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); EXPECT_EQ(extra.width() + border.left(), text_bounds.x()); EXPECT_EQ(border.top() + extra.height() / 2, text_bounds.y()); EXPECT_EQ(center_bounds.width(), text_bounds.width()); @@ -590,11 +603,10 @@ TEST(LabelTest, DrawMultiLineString) { expected_flags = gfx::Canvas::MULTI_LINE | gfx::Canvas::TEXT_ALIGN_RIGHT | gfx::Canvas::FORCE_LTR_DIRECTIONALITY; -#if defined(OS_WIN) - EXPECT_EQ(expected_flags, flags); -#else - EXPECT_EQ(expected_flags | gfx::Canvas::NO_ELLIPSIS, flags); +#if !defined(OS_WIN) + expected_flags |= gfx::Canvas::NO_ELLIPSIS; #endif + EXPECT_EQ(expected_flags, expected_flags); } TEST(LabelTest, DrawSingleLineStringInRTL) { @@ -604,23 +616,20 @@ TEST(LabelTest, DrawSingleLineStringInRTL) { std::string locale = l10n_util::GetApplicationLocale(""); base::i18n::SetICUDefaultLocale("he"); - string16 test_text(ASCIIToUTF16("Here's a string with no returns.")); - label.SetText(test_text); + label.SetText(ASCIIToUTF16("Here's a string with no returns.")); gfx::Size required_size(label.GetPreferredSize()); gfx::Size extra(22, 8); - label.SetBounds(0, - 0, - required_size.width() + extra.width(), + label.SetBounds(0, 0, required_size.width() + extra.width(), required_size.height() + extra.height()); // Do some basic verifications for all three alignments. - string16 paint_text; + base::string16 paint_text; gfx::Rect text_bounds; - int flags; + int flags = -1; // Centered text. label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); // The text should be centered horizontally and vertically. EXPECT_EQ(extra.width() / 2, text_bounds.x()); EXPECT_EQ(extra.height() / 2 , text_bounds.y()); @@ -636,7 +645,7 @@ TEST(LabelTest, DrawSingleLineStringInRTL) { paint_text.clear(); text_bounds.SetRect(0, 0, 0, 0); label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); // The text should be right aligned horizontally and centered vertically. EXPECT_EQ(extra.width(), text_bounds.x()); EXPECT_EQ(extra.height() / 2 , text_bounds.y()); @@ -652,7 +661,7 @@ TEST(LabelTest, DrawSingleLineStringInRTL) { paint_text.clear(); text_bounds.SetRect(0, 0, 0, 0); label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); // The text should be left aligned horizontally and centered vertically. EXPECT_EQ(0, text_bounds.x()); EXPECT_EQ(extra.height() / 2 , text_bounds.y()); @@ -666,19 +675,15 @@ TEST(LabelTest, DrawSingleLineStringInRTL) { // Test single line drawing with a border. gfx::Insets border(39, 34, 8, 96); - label.set_border(Border::CreateEmptyBorder(border.top(), - border.left(), - border.bottom(), - border.right())); + label.SetBorder(Border::CreateEmptyBorder( + border.top(), border.left(), border.bottom(), border.right())); gfx::Size required_size_with_border(label.GetPreferredSize()); EXPECT_EQ(required_size.width() + border.width(), required_size_with_border.width()); EXPECT_EQ(required_size.height() + border.height(), required_size_with_border.height()); - label.SetBounds(0, - 0, - required_size_with_border.width() + extra.width(), + label.SetBounds(0, 0, required_size_with_border.width() + extra.width(), required_size_with_border.height() + extra.height()); // Centered text with border. @@ -686,7 +691,7 @@ TEST(LabelTest, DrawSingleLineStringInRTL) { paint_text.clear(); text_bounds.SetRect(0, 0, 0, 0); label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); // The text should be centered horizontally and vertically within the border. EXPECT_EQ(border.left() + extra.width() / 2, text_bounds.x()); EXPECT_EQ(border.top() + extra.height() / 2 , text_bounds.y()); @@ -702,7 +707,7 @@ TEST(LabelTest, DrawSingleLineStringInRTL) { paint_text.clear(); text_bounds.SetRect(0, 0, 0, 0); label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); // The text should be right aligned horizontally and centered vertically. EXPECT_EQ(border.left() + extra.width(), text_bounds.x()); EXPECT_EQ(border.top() + extra.height() / 2 , text_bounds.y()); @@ -718,7 +723,7 @@ TEST(LabelTest, DrawSingleLineStringInRTL) { paint_text.clear(); text_bounds.SetRect(0, 0, 0, 0); label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); // The text should be left aligned horizontally and centered vertically. EXPECT_EQ(border.left(), text_bounds.x()); EXPECT_EQ(border.top() + extra.height() / 2 , text_bounds.y()); @@ -744,34 +749,28 @@ TEST(LabelTest, DrawMultiLineStringInRTL) { std::string locale = l10n_util::GetApplicationLocale(""); base::i18n::SetICUDefaultLocale("he"); - string16 test_text(ASCIIToUTF16("Another string\nwith returns\n\n!")); - label.SetText(test_text); + label.SetText(ASCIIToUTF16("Another string\nwith returns\n\n!")); label.SetMultiLine(true); label.SizeToFit(0); gfx::Size extra(50, 10); - label.SetBounds(label.x(), - label.y(), + label.SetBounds(label.x(), label.y(), label.width() + extra.width(), label.height() + extra.height()); // Do some basic verifications for all three alignments. - string16 paint_text; + base::string16 paint_text; gfx::Rect text_bounds; - int flags; + int flags = -1; label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); EXPECT_EQ(extra.width() / 2, text_bounds.x()); EXPECT_EQ(extra.height() / 2, text_bounds.y()); EXPECT_GT(text_bounds.width(), kMinTextDimension); EXPECT_GT(text_bounds.height(), kMinTextDimension); -#if defined(OS_WIN) - EXPECT_EQ(gfx::Canvas::MULTI_LINE | gfx::Canvas::TEXT_ALIGN_CENTER, flags); -#else - EXPECT_EQ( - gfx::Canvas::MULTI_LINE | - gfx::Canvas::TEXT_ALIGN_CENTER | - gfx::Canvas::NO_ELLIPSIS, - flags); + EXPECT_TRUE(gfx::Canvas::MULTI_LINE & flags); + EXPECT_TRUE(gfx::Canvas::TEXT_ALIGN_CENTER & flags); +#if !defined(OS_WIN) + EXPECT_TRUE(gfx::Canvas::NO_ELLIPSIS & flags); #endif gfx::Rect center_bounds(text_bounds); @@ -779,49 +778,38 @@ TEST(LabelTest, DrawMultiLineStringInRTL) { paint_text.clear(); text_bounds.SetRect(0, 0, 0, 0); label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); EXPECT_EQ(extra.width(), text_bounds.x()); EXPECT_EQ(extra.height() / 2, text_bounds.y()); EXPECT_GT(text_bounds.width(), kMinTextDimension); EXPECT_GT(text_bounds.height(), kMinTextDimension); -#if defined(OS_WIN) - EXPECT_EQ(gfx::Canvas::MULTI_LINE | gfx::Canvas::TEXT_ALIGN_RIGHT, flags); -#else - EXPECT_EQ( - gfx::Canvas::MULTI_LINE | - gfx::Canvas::TEXT_ALIGN_RIGHT | - gfx::Canvas::NO_ELLIPSIS, - flags); + EXPECT_TRUE(gfx::Canvas::MULTI_LINE & flags); + EXPECT_TRUE(gfx::Canvas::TEXT_ALIGN_RIGHT & flags); +#if !defined(OS_WIN) + EXPECT_TRUE(gfx::Canvas::NO_ELLIPSIS & flags); #endif label.SetHorizontalAlignment(gfx::ALIGN_RIGHT); paint_text.clear(); text_bounds.SetRect(0, 0, 0, 0); label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); EXPECT_EQ(0, text_bounds.x()); EXPECT_EQ(extra.height() / 2, text_bounds.y()); EXPECT_GT(text_bounds.width(), kMinTextDimension); EXPECT_GT(text_bounds.height(), kMinTextDimension); -#if defined(OS_WIN) - EXPECT_EQ(gfx::Canvas::MULTI_LINE | gfx::Canvas::TEXT_ALIGN_LEFT, flags); -#else - EXPECT_EQ( - gfx::Canvas::MULTI_LINE | - gfx::Canvas::TEXT_ALIGN_LEFT | - gfx::Canvas::NO_ELLIPSIS, - flags); + EXPECT_TRUE(gfx::Canvas::MULTI_LINE & flags); + EXPECT_TRUE(gfx::Canvas::TEXT_ALIGN_LEFT & flags); +#if !defined(OS_WIN) + EXPECT_TRUE(gfx::Canvas::NO_ELLIPSIS & flags); #endif // Test multiline drawing with a border. gfx::Insets border(19, 92, 23, 2); - label.set_border(Border::CreateEmptyBorder(border.top(), - border.left(), - border.bottom(), - border.right())); + label.SetBorder(Border::CreateEmptyBorder( + border.top(), border.left(), border.bottom(), border.right())); label.SizeToFit(0); - label.SetBounds(label.x(), - label.y(), + label.SetBounds(label.x(), label.y(), label.width() + extra.width(), label.height() + extra.height()); @@ -829,75 +817,64 @@ TEST(LabelTest, DrawMultiLineStringInRTL) { paint_text.clear(); text_bounds.SetRect(0, 0, 0, 0); label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); EXPECT_EQ(border.left() + extra.width() / 2, text_bounds.x()); EXPECT_EQ(border.top() + extra.height() / 2, text_bounds.y()); EXPECT_EQ(center_bounds.width(), text_bounds.width()); EXPECT_EQ(center_bounds.height(), text_bounds.height()); -#if defined(OS_WIN) - EXPECT_EQ(gfx::Canvas::MULTI_LINE | gfx::Canvas::TEXT_ALIGN_CENTER, flags); -#else - EXPECT_EQ( - gfx::Canvas::MULTI_LINE | - gfx::Canvas::TEXT_ALIGN_CENTER | - gfx::Canvas::NO_ELLIPSIS, - flags); + EXPECT_TRUE(gfx::Canvas::MULTI_LINE & flags); + EXPECT_TRUE(gfx::Canvas::TEXT_ALIGN_CENTER & flags); +#if !defined(OS_WIN) + EXPECT_TRUE(gfx::Canvas::NO_ELLIPSIS & flags); #endif label.SetHorizontalAlignment(gfx::ALIGN_LEFT); paint_text.clear(); text_bounds.SetRect(0, 0, 0, 0); label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); EXPECT_EQ(border.left() + extra.width(), text_bounds.x()); EXPECT_EQ(border.top() + extra.height() / 2, text_bounds.y()); EXPECT_EQ(center_bounds.width(), text_bounds.width()); EXPECT_EQ(center_bounds.height(), text_bounds.height()); -#if defined(OS_WIN) - EXPECT_EQ(gfx::Canvas::MULTI_LINE | gfx::Canvas::TEXT_ALIGN_RIGHT, flags); -#else - EXPECT_EQ( - gfx::Canvas::MULTI_LINE | - gfx::Canvas::TEXT_ALIGN_RIGHT | - gfx::Canvas::NO_ELLIPSIS, - flags); + EXPECT_TRUE(gfx::Canvas::MULTI_LINE & flags); + EXPECT_TRUE(gfx::Canvas::TEXT_ALIGN_RIGHT & flags); +#if !defined(OS_WIN) + EXPECT_TRUE(gfx::Canvas::NO_ELLIPSIS & flags); #endif label.SetHorizontalAlignment(gfx::ALIGN_RIGHT); paint_text.clear(); text_bounds.SetRect(0, 0, 0, 0); label.CalculateDrawStringParams(&paint_text, &text_bounds, &flags); - EXPECT_EQ(test_text, paint_text); + EXPECT_EQ(label.text(), paint_text); EXPECT_EQ(border.left(), text_bounds.x()); EXPECT_EQ(border.top() + extra.height() / 2, text_bounds.y()); EXPECT_EQ(center_bounds.width(), text_bounds.width()); EXPECT_EQ(center_bounds.height(), text_bounds.height()); -#if defined(OS_WIN) - EXPECT_EQ(gfx::Canvas::MULTI_LINE | gfx::Canvas::TEXT_ALIGN_LEFT, flags); -#else - EXPECT_EQ( - gfx::Canvas::MULTI_LINE | - gfx::Canvas::TEXT_ALIGN_LEFT | - gfx::Canvas::NO_ELLIPSIS, - flags); + EXPECT_TRUE(gfx::Canvas::MULTI_LINE & flags); + EXPECT_TRUE(gfx::Canvas::TEXT_ALIGN_LEFT & flags); +#if !defined(OS_WIN) + EXPECT_TRUE(gfx::Canvas::NO_ELLIPSIS & flags); #endif // Reset Locale base::i18n::SetICUDefaultLocale(locale); } -// Check that we disable subpixel rendering when a transparent background is -// being used. +// Ensure the subpixel rendering flag and background color alpha are respected. TEST(LabelTest, DisableSubpixelRendering) { Label label; label.SetBackgroundColor(SK_ColorWHITE); - EXPECT_EQ( - 0, label.ComputeDrawStringFlags() & gfx::Canvas::NO_SUBPIXEL_RENDERING); - + const int flag = gfx::Canvas::NO_SUBPIXEL_RENDERING; + EXPECT_EQ(0, label.ComputeDrawStringFlags() & flag); + label.set_subpixel_rendering_enabled(false); + EXPECT_EQ(flag, label.ComputeDrawStringFlags() & flag); + label.set_subpixel_rendering_enabled(true); + EXPECT_EQ(0, label.ComputeDrawStringFlags() & flag); + // Text cannot be drawn with subpixel rendering on transparent backgrounds. label.SetBackgroundColor(SkColorSetARGB(64, 255, 255, 255)); - EXPECT_EQ( - gfx::Canvas::NO_SUBPIXEL_RENDERING, - label.ComputeDrawStringFlags() & gfx::Canvas::NO_SUBPIXEL_RENDERING); + EXPECT_EQ(flag, label.ComputeDrawStringFlags() & flag); } // Check that labels support GetTooltipHandlerForPoint. diff --git a/chromium/ui/views/controls/link.cc b/chromium/ui/views/controls/link.cc index d02a0b89c73..d8a08a40c92 100644 --- a/chromium/ui/views/controls/link.cc +++ b/chromium/ui/views/controls/link.cc @@ -8,27 +8,25 @@ #include "base/logging.h" #include "base/strings/utf_string_conversions.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" +#include "ui/base/cursor/cursor.h" #include "ui/events/event.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/canvas.h" #include "ui/gfx/color_utils.h" -#include "ui/gfx/font.h" +#include "ui/gfx/font_list.h" #include "ui/views/controls/link_listener.h" - -#if defined(USE_AURA) -#include "ui/base/cursor/cursor.h" -#endif +#include "ui/views/native_cursor.h" namespace views { const char Link::kViewClassName[] = "Link"; -Link::Link() : Label(string16()) { +Link::Link() : Label(base::string16()) { Init(); } -Link::Link(const string16& title) : Label(title) { +Link::Link(const base::string16& title) : Label(title) { Init(); } @@ -43,11 +41,6 @@ SkColor Link::GetDefaultEnabledColor() { #endif } -void Link::OnEnabledChanged() { - RecalculateFont(); - View::OnEnabledChanged(); -} - const char* Link::GetClassName() const { return kViewClassName; } @@ -55,37 +48,13 @@ const char* Link::GetClassName() const { gfx::NativeCursor Link::GetCursor(const ui::MouseEvent& event) { if (!enabled()) return gfx::kNullCursor; -#if defined(USE_AURA) - return ui::kCursorHand; -#elif defined(OS_WIN) - static HCURSOR g_hand_cursor = LoadCursor(NULL, IDC_HAND); - return g_hand_cursor; -#endif -} - -void Link::OnPaint(gfx::Canvas* canvas) { - Label::OnPaint(canvas); - - if (HasFocus()) - canvas->DrawFocusRect(GetLocalBounds()); -} - -void Link::OnFocus() { - Label::OnFocus(); - // We render differently focused. - SchedulePaint(); -} - -void Link::OnBlur() { - Label::OnBlur(); - // We render differently focused. - SchedulePaint(); + return GetNativeHandCursor(); } -bool Link::HitTestRect(const gfx::Rect& rect) const { - // We need to allow clicks on the link. So we override the implementation in - // Label and use the default implementation of View. - return View::HitTestRect(rect); +bool Link::CanProcessEventsWithinSubtree() const { + // Links need to be able to accept events (e.g., clicking) even though + // in general Labels do not. + return View::CanProcessEventsWithinSubtree(); } bool Link::OnMousePressed(const ui::MouseEvent& event) { @@ -139,17 +108,6 @@ bool Link::OnKeyPressed(const ui::KeyEvent& event) { return true; } -bool Link::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) { - // Make sure we don't process space or enter as accelerators. - return (event.key_code() == ui::VKEY_SPACE) || - (event.key_code() == ui::VKEY_RETURN); -} - -void Link::GetAccessibleState(ui::AccessibleViewState* state) { - Label::GetAccessibleState(state); - state->role = ui::AccessibilityTypes::ROLE_LINK; -} - void Link::OnGestureEvent(ui::GestureEvent* event) { if (!enabled()) return; @@ -167,9 +125,45 @@ void Link::OnGestureEvent(ui::GestureEvent* event) { event->SetHandled(); } -void Link::SetFont(const gfx::Font& font) { - Label::SetFont(font); +bool Link::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) { + // Make sure we don't process space or enter as accelerators. + return (event.key_code() == ui::VKEY_SPACE) || + (event.key_code() == ui::VKEY_RETURN); +} + +void Link::GetAccessibleState(ui::AXViewState* state) { + Label::GetAccessibleState(state); + state->role = ui::AX_ROLE_LINK; +} + +void Link::OnEnabledChanged() { RecalculateFont(); + View::OnEnabledChanged(); +} + +void Link::OnFocus() { + Label::OnFocus(); + // We render differently focused. + SchedulePaint(); +} + +void Link::OnBlur() { + Label::OnBlur(); + // We render differently focused. + SchedulePaint(); +} + +void Link::SetFontList(const gfx::FontList& font_list) { + Label::SetFontList(font_list); + RecalculateFont(); +} + +void Link::SetText(const base::string16& text) { + Label::SetText(text); + // Disable focusability for empty links. Otherwise Label::GetInsets() will + // give them an unconditional 1-px. inset on every side to allow for a focus + // border, when in this case we probably wanted zero width. + SetFocusable(!text.empty()); } void Link::SetEnabledColor(SkColor color) { @@ -205,7 +199,12 @@ void Link::Init() { SetPressedColor(SK_ColorRED); #endif RecalculateFont(); - SetFocusable(true); + + // Label::Init() calls SetText(), but if that's being called from Label(), our + // SetText() override will not be reached (because the constructed class is + // only a Label at the moment, not yet a Link). So so the set_focusable() + // call explicitly here. + SetFocusable(!text().empty()); } void Link::SetPressed(bool pressed) { @@ -220,11 +219,11 @@ void Link::SetPressed(bool pressed) { void Link::RecalculateFont() { // Underline the link iff it is enabled and |underline_| is true. - const int style = font().GetStyle(); + const int style = font_list().GetFontStyle(); const int intended_style = (enabled() && underline_) ? (style | gfx::Font::UNDERLINE) : (style & ~gfx::Font::UNDERLINE); if (style != intended_style) - Label::SetFont(font().DeriveFont(0, intended_style)); + Label::SetFontList(font_list().DeriveWithStyle(intended_style)); } } // namespace views diff --git a/chromium/ui/views/controls/link.h b/chromium/ui/views/controls/link.h index f6a876de863..4d5cf6b7afa 100644 --- a/chromium/ui/views/controls/link.h +++ b/chromium/ui/views/controls/link.h @@ -25,7 +25,7 @@ class LinkListener; class VIEWS_EXPORT Link : public Label { public: Link(); - explicit Link(const string16& title); + explicit Link(const base::string16& title); virtual ~Link(); static SkColor GetDefaultEnabledColor(); @@ -33,30 +33,26 @@ class VIEWS_EXPORT Link : public Label { const LinkListener* listener() { return listener_; } void set_listener(LinkListener* listener) { listener_ = listener; } - // Overridden from View: - virtual void OnEnabledChanged() OVERRIDE; + // Label: virtual const char* GetClassName() const OVERRIDE; virtual gfx::NativeCursor GetCursor(const ui::MouseEvent& event) OVERRIDE; - virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; - virtual void OnFocus() OVERRIDE; - virtual void OnBlur() OVERRIDE; - virtual bool HitTestRect(const gfx::Rect& rect) const OVERRIDE; + virtual bool CanProcessEventsWithinSubtree() const 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 bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE; + virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; virtual bool SkipDefaultKeyEventProcessing( const ui::KeyEvent& event) OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; - - // Overridden from ui::EventHandler: - virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; - - // Overridden from Label: - virtual void SetFont(const gfx::Font& font) OVERRIDE; - + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; + virtual void OnEnabledChanged() OVERRIDE; + virtual void OnFocus() OVERRIDE; + virtual void OnBlur() OVERRIDE; + virtual void SetFontList(const gfx::FontList& font_list) OVERRIDE; + virtual void SetText(const base::string16& text) OVERRIDE; virtual void SetEnabledColor(SkColor color) OVERRIDE; + void SetPressedColor(SkColor color); void SetUnderline(bool underline); diff --git a/chromium/ui/views/controls/menu/display_change_listener_mac.cc b/chromium/ui/views/controls/menu/display_change_listener_mac.cc new file mode 100644 index 00000000000..d9dfeda5cfd --- /dev/null +++ b/chromium/ui/views/controls/menu/display_change_listener_mac.cc @@ -0,0 +1,16 @@ +// Copyright 2014 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/controls/menu/menu_runner.h" + +namespace views { +namespace internal { + +// static +DisplayChangeListener* DisplayChangeListener::Create(Widget*, MenuRunner*) { + return NULL; +} + +} // namespace internal +} // namespace views diff --git a/chromium/ui/views/controls/menu/menu.cc b/chromium/ui/views/controls/menu/menu.cc index 597dba4419f..0cffae8f50e 100644 --- a/chromium/ui/views/controls/menu/menu.cc +++ b/chromium/ui/views/controls/menu/menu.cc @@ -17,8 +17,8 @@ bool Menu::Delegate::IsItemDefault(int id) const { return false; } -string16 Menu::Delegate::GetLabel(int id) const { - return string16(); +base::string16 Menu::Delegate::GetLabel(int id) const { + return base::string16(); } bool Menu::Delegate::GetAcceleratorInfo(int id, ui::Accelerator* accel) { @@ -49,7 +49,7 @@ bool Menu::Delegate::IsCommandEnabled(int id) const { return true; } -bool Menu::Delegate::GetContextualLabel(int id, string16* out) const { +bool Menu::Delegate::GetContextualLabel(int id, base::string16* out) const { return false; } @@ -76,14 +76,14 @@ Menu::~Menu() { } void Menu::AppendMenuItem(int item_id, - const string16& label, + const base::string16& label, MenuItemType type) { AddMenuItem(-1, item_id, label, type); } void Menu::AddMenuItem(int index, int item_id, - const string16& label, + const base::string16& label, MenuItemType type) { if (type == SEPARATOR) AddSeparator(index); @@ -91,27 +91,27 @@ void Menu::AddMenuItem(int index, AddMenuItemInternal(index, item_id, label, gfx::ImageSkia(), type); } -Menu* Menu::AppendSubMenu(int item_id, const string16& label) { +Menu* Menu::AppendSubMenu(int item_id, const base::string16& label) { return AddSubMenu(-1, item_id, label); } -Menu* Menu::AddSubMenu(int index, int item_id, const string16& label) { +Menu* Menu::AddSubMenu(int index, int item_id, const base::string16& label) { return AddSubMenuWithIcon(index, item_id, label, gfx::ImageSkia()); } Menu* Menu::AppendSubMenuWithIcon(int item_id, - const string16& label, + const base::string16& label, const gfx::ImageSkia& icon) { return AddSubMenuWithIcon(-1, item_id, label, icon); } -void Menu::AppendMenuItemWithLabel(int item_id, const string16& label) { +void Menu::AppendMenuItemWithLabel(int item_id, const base::string16& label) { AddMenuItemWithLabel(-1, item_id, label); } void Menu::AddMenuItemWithLabel(int index, int item_id, - const string16& label) { + const base::string16& label) { AddMenuItem(index, item_id, label, Menu::NORMAL); } @@ -120,7 +120,7 @@ void Menu::AppendDelegateMenuItem(int item_id) { } void Menu::AddDelegateMenuItem(int index, int item_id) { - AddMenuItem(index, item_id, string16(), Menu::NORMAL); + AddMenuItem(index, item_id, base::string16(), Menu::NORMAL); } void Menu::AppendSeparator() { @@ -128,14 +128,14 @@ void Menu::AppendSeparator() { } void Menu::AppendMenuItemWithIcon(int item_id, - const string16& label, + const base::string16& label, const gfx::ImageSkia& icon) { AddMenuItemWithIcon(-1, item_id, label, icon); } void Menu::AddMenuItemWithIcon(int index, int item_id, - const string16& label, + const base::string16& label, const gfx::ImageSkia& icon) { AddMenuItemInternal(index, item_id, label, icon, Menu::NORMAL); } diff --git a/chromium/ui/views/controls/menu/menu.h b/chromium/ui/views/controls/menu/menu.h index 5a774df7320..099a98ee543 100644 --- a/chromium/ui/views/controls/menu/menu.h +++ b/chromium/ui/views/controls/menu/menu.h @@ -45,7 +45,7 @@ class VIEWS_EXPORT Menu { virtual bool IsItemDefault(int id) const; // The string shown for the menu item. - virtual string16 GetLabel(int id) const; + virtual base::string16 GetLabel(int id) const; // The delegate needs to implement this function if it wants to display // the shortcut text next to each menu item. If there is an accelerator @@ -94,7 +94,7 @@ class VIEWS_EXPORT Menu { // Controller virtual bool SupportsCommand(int id) const; virtual bool IsCommandEnabled(int id) const; - virtual bool GetContextualLabel(int id, string16* out) const; + virtual bool GetContextualLabel(int id, base::string16* out) const; virtual void ExecuteCommand(int id) { } @@ -151,35 +151,37 @@ class VIEWS_EXPORT Menu { // label The text label shown. // type The type of item. void AppendMenuItem(int item_id, - const string16& label, + const base::string16& label, MenuItemType type); void AddMenuItem(int index, int item_id, - const string16& label, + const base::string16& label, MenuItemType type); // Append a submenu to this menu. // The returned pointer is owned by this menu. Menu* AppendSubMenu(int item_id, - const string16& label); - Menu* AddSubMenu(int index, int item_id, const string16& label); + const base::string16& label); + Menu* AddSubMenu(int index, int item_id, const base::string16& label); // Append a submenu with an icon to this menu // The returned pointer is owned by this menu. // Unless the icon is empty, calling this function forces the Menu class // to draw the menu, instead of relying on Windows. Menu* AppendSubMenuWithIcon(int item_id, - const string16& label, + const base::string16& label, const gfx::ImageSkia& icon); virtual Menu* AddSubMenuWithIcon(int index, int item_id, - const string16& label, + const base::string16& label, const gfx::ImageSkia& icon) = 0; // This is a convenience for standard text label menu items where the label // is provided with this call. - void AppendMenuItemWithLabel(int item_id, const string16& label); - void AddMenuItemWithLabel(int index, int item_id, const string16& label); + void AppendMenuItemWithLabel(int item_id, const base::string16& label); + void AddMenuItemWithLabel(int index, + int item_id, + const base::string16& label); // This is a convenience for text label menu items where the label is // provided by the delegate. @@ -194,11 +196,11 @@ class VIEWS_EXPORT Menu { // needs an icon. Calling this function forces the Menu class to draw // the menu, instead of relying on Windows. void AppendMenuItemWithIcon(int item_id, - const string16& label, + const base::string16& label, const gfx::ImageSkia& icon); virtual void AddMenuItemWithIcon(int index, int item_id, - const string16& label, + const base::string16& label, const gfx::ImageSkia& icon); // Enables or disables the item with the specified id. @@ -206,7 +208,7 @@ class VIEWS_EXPORT Menu { virtual void EnableMenuItemAt(int index, bool enabled) = 0; // Sets menu label at specified index. - virtual void SetMenuLabel(int item_id, const string16& label) = 0; + virtual void SetMenuLabel(int item_id, const base::string16& label) = 0; // Sets an icon for an item with a given item_id. Calling this function // also forces the Menu class to draw the menu, instead of relying on Windows. @@ -234,7 +236,7 @@ class VIEWS_EXPORT Menu { virtual void AddMenuItemInternal(int index, int item_id, - const string16& label, + const base::string16& label, const gfx::ImageSkia& icon, MenuItemType type) = 0; diff --git a/chromium/ui/views/controls/menu/menu_config.cc b/chromium/ui/views/controls/menu/menu_config.cc index 1c34ac0b3e0..9eaba83f6db 100644 --- a/chromium/ui/views/controls/menu/menu_config.cc +++ b/chromium/ui/views/controls/menu/menu_config.cc @@ -5,14 +5,11 @@ #include "ui/views/controls/menu/menu_config.h" #include "build/build_config.h" -#include "ui/base/layout.h" -#include "ui/native_theme/native_theme.h" namespace views { MenuConfig::MenuConfig(const ui::NativeTheme* theme) - : text_color(SK_ColorBLACK), - arrow_color(SK_ColorBLACK), + : arrow_color(SK_ColorBLACK), menu_vertical_border_size(3), menu_horizontal_border_size(0), submenu_horizontal_inset(3), @@ -28,8 +25,6 @@ MenuConfig::MenuConfig(const ui::NativeTheme* theme) check_width(16), check_height(16), radio_width(16), - radio_height(16), - arrow_height(9), arrow_width(9), gutter_width(0), separator_height(11), @@ -48,12 +43,6 @@ MenuConfig::MenuConfig(const ui::NativeTheme* theme) native_theme(theme), show_delay(400), corner_radius(0) { - // Use 40px tall menu items when running in touch optimized mode. - // For Windows use 40px tall menu items when running in touch optimized mode. - if (ui::GetDisplayLayout() == ui::LAYOUT_TOUCH) { - item_top_margin = item_no_icon_top_margin = 12; - item_bottom_margin = item_no_icon_bottom_margin = 13; - } Init(theme); } diff --git a/chromium/ui/views/controls/menu/menu_config.h b/chromium/ui/views/controls/menu/menu_config.h index 96eb21ac219..3cdbccdbf4d 100644 --- a/chromium/ui/views/controls/menu/menu_config.h +++ b/chromium/ui/views/controls/menu/menu_config.h @@ -6,7 +6,7 @@ #define UI_VIEWS_CONTROLS_MENU_MENU_CONFIG_H_ #include "third_party/skia/include/core/SkColor.h" -#include "ui/gfx/font.h" +#include "ui/gfx/font_list.h" #include "ui/views/views_export.h" namespace ui { @@ -23,11 +23,8 @@ struct VIEWS_EXPORT MenuConfig { static const MenuConfig& instance(const ui::NativeTheme* theme); - // Font used by menus. - gfx::Font font; - - // Normal text color. - SkColor text_color; + // Font list used by menus. + gfx::FontList font_list; // Color for the arrow to scroll bookmarks. SkColor arrow_color; @@ -70,12 +67,10 @@ struct VIEWS_EXPORT MenuConfig { int check_width; int check_height; - // Size of the radio bullet. + // Width of the radio bullet. int radio_width; - int radio_height; - // Size of the submenu arrow. - int arrow_height; + // Width of the submenu arrow. int arrow_width; // Width of the gutter. Only used if render_gutter is true. @@ -136,9 +131,7 @@ struct VIEWS_EXPORT MenuConfig { void Init(const ui::NativeTheme* theme); // TODO: temporary until we standardize. -#if defined(USE_AURA) void InitAura(const ui::NativeTheme* theme); -#endif }; } // namespace views diff --git a/chromium/ui/views/controls/menu/menu_config_views.cc b/chromium/ui/views/controls/menu/menu_config_aura.cc index d0b48443711..2e464d22488 100644 --- a/chromium/ui/views/controls/menu/menu_config_views.cc +++ b/chromium/ui/views/controls/menu/menu_config_aura.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2014 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. @@ -30,20 +30,16 @@ void MenuConfig::Init(const ui::NativeTheme* theme) { #endif void MenuConfig::InitAura(const ui::NativeTheme* theme) { - text_color = theme->GetSystemColor( - ui::NativeTheme::kColorId_EnabledMenuItemForegroundColor); submenu_horizontal_inset = 1; arrow_to_edge_padding = 20; ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - arrow_width = - rb.GetImageNamed(IDR_MENU_HIERARCHY_ARROW).ToImageSkia()->width(); + arrow_width = rb.GetImageNamed(IDR_MENU_HIERARCHY_ARROW).Width(); gfx::ImageSkia check = GetMenuCheckImage(false); check_height = check.height(); item_min_height = 29; separator_spacing_height = 7; separator_lower_height = 8; separator_upper_height = 8; - font = rb.GetFont(ResourceBundle::BaseFont); label_to_arrow_padding = 20; label_to_minor_text_padding = 20; always_use_icon_to_label_padding = true; diff --git a/chromium/ui/views/controls/menu/menu_config_mac.cc b/chromium/ui/views/controls/menu/menu_config_mac.cc new file mode 100644 index 00000000000..dd38e72fe7e --- /dev/null +++ b/chromium/ui/views/controls/menu/menu_config_mac.cc @@ -0,0 +1,26 @@ +// Copyright 2014 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/controls/menu/menu_config.h" + +#include "grit/ui_resources.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/native_theme/native_theme_mac.h" +#include "ui/views/controls/menu/menu_image_util.h" + +namespace views { + +void MenuConfig::Init(const ui::NativeTheme* theme) { + NOTIMPLEMENTED(); +} + +// static +const MenuConfig& MenuConfig::instance(const ui::NativeTheme* theme) { + CR_DEFINE_STATIC_LOCAL( + MenuConfig, mac_instance, (theme ? theme : ui::NativeTheme::instance())); + return mac_instance; +} + +} // namespace views diff --git a/chromium/ui/views/controls/menu/menu_config_win.cc b/chromium/ui/views/controls/menu/menu_config_win.cc index e0bde54c30b..4ef5fac3070 100644 --- a/chromium/ui/views/controls/menu/menu_config_win.cc +++ b/chromium/ui/views/controls/menu/menu_config_win.cc @@ -13,11 +13,8 @@ #include "base/win/win_util.h" #include "ui/base/l10n/l10n_util_win.h" #include "ui/gfx/color_utils.h" -#include "ui/native_theme/native_theme_win.h" - -#if defined(USE_AURA) #include "ui/native_theme/native_theme_aura.h" -#endif +#include "ui/native_theme/native_theme_win.h" using ui::NativeTheme; using ui::NativeThemeWin; @@ -25,15 +22,10 @@ using ui::NativeThemeWin; namespace views { void MenuConfig::Init(const NativeTheme* theme) { -#if defined(USE_AURA) if (theme == ui::NativeThemeAura::instance()) { InitAura(theme); return; } -#endif - text_color = NativeThemeWin::instance()->GetThemeColorWithDefault( - NativeThemeWin::MENU, MENU_POPUPITEM, MPI_NORMAL, TMT_TEXTCOLOR, - COLOR_MENUTEXT); arrow_color = color_utils::GetSysSkColor(COLOR_MENUTEXT); @@ -43,7 +35,7 @@ void MenuConfig::Init(const NativeTheme* theme) { { base::win::ScopedHFONT new_font(CreateFontIndirect(&metrics.lfMenuFont)); DLOG_ASSERT(new_font.Get()); - font = gfx::Font(new_font); + font_list = gfx::FontList(gfx::Font(new_font)); } NativeTheme::ExtraParams extra; extra.menu_check.is_radio = false; @@ -61,23 +53,18 @@ void MenuConfig::Init(const NativeTheme* theme) { extra.menu_check.is_radio = true; gfx::Size radio_size = NativeThemeWin::instance()->GetPartSize( NativeTheme::kMenuCheck, NativeTheme::kNormal, extra); - if (!radio_size.IsEmpty()) { + if (!radio_size.IsEmpty()) radio_width = radio_size.width(); - radio_height = radio_size.height(); - } else { + else radio_width = GetSystemMetrics(SM_CXMENUCHECK); - radio_height = GetSystemMetrics(SM_CYMENUCHECK); - } gfx::Size arrow_size = NativeThemeWin::instance()->GetPartSize( NativeTheme::kMenuPopupArrow, NativeTheme::kNormal, extra); if (!arrow_size.IsEmpty()) { arrow_width = arrow_size.width(); - arrow_height = arrow_size.height(); } else { // Sadly I didn't see a specify metrics for this. arrow_width = GetSystemMetrics(SM_CXMENUCHECK); - arrow_height = GetSystemMetrics(SM_CYMENUCHECK); } BOOL show_cues; diff --git a/chromium/ui/views/controls/menu/menu_controller.cc b/chromium/ui/views/controls/menu/menu_controller.cc index 3b0e5d552c0..d5b5a84e8f1 100644 --- a/chromium/ui/views/controls/menu/menu_controller.cc +++ b/chromium/ui/views/controls/menu/menu_controller.cc @@ -4,23 +4,18 @@ #include "ui/views/controls/menu/menu_controller.h" -#if defined(OS_WIN) -#include <windowsx.h> -#endif - #include "base/i18n/case_conversion.h" #include "base/i18n/rtl.h" -#include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "ui/base/dragdrop/drag_utils.h" #include "ui/base/dragdrop/os_exchange_data.h" #include "ui/base/l10n/l10n_util.h" -#include "ui/events/event_constants.h" +#include "ui/events/event.h" #include "ui/events/event_utils.h" -#include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/canvas.h" #include "ui/gfx/native_widget_types.h" +#include "ui/gfx/point.h" #include "ui/gfx/screen.h" #include "ui/gfx/vector2d.h" #include "ui/native_theme/native_theme.h" @@ -28,32 +23,26 @@ #include "ui/views/controls/menu/menu_config.h" #include "ui/views/controls/menu/menu_controller_delegate.h" #include "ui/views/controls/menu/menu_host_root_view.h" +#include "ui/views/controls/menu/menu_item_view.h" +#include "ui/views/controls/menu/menu_message_loop.h" #include "ui/views/controls/menu/menu_scroll_view_container.h" #include "ui/views/controls/menu/submenu_view.h" #include "ui/views/drag_utils.h" -#include "ui/views/event_utils.h" #include "ui/views/focus/view_storage.h" #include "ui/views/mouse_constants.h" +#include "ui/views/view.h" #include "ui/views/view_constants.h" #include "ui/views/views_delegate.h" #include "ui/views/widget/root_view.h" #include "ui/views/widget/tooltip_manager.h" #include "ui/views/widget/widget.h" -#if defined(USE_AURA) -#include "ui/aura/env.h" -#include "ui/aura/root_window.h" -#endif - #if defined(OS_WIN) -#include "ui/views/win/hwnd_message_handler.h" +#include "ui/base/win/internal_constants.h" +#include "ui/gfx/win/dpi.h" #include "ui/views/win/hwnd_util.h" #endif -#if defined(USE_X11) -#include <X11/Xlib.h> -#endif - using base::Time; using base::TimeDelta; using ui::OSExchangeData; @@ -87,17 +76,17 @@ const int kBubbleTipSizeTopBottom = 11; const float kMaximumLengthMovedToActivate = 4.0f; // Returns true if the mnemonic of |menu| matches key. -bool MatchesMnemonic(MenuItemView* menu, char16 key) { - return menu->GetMnemonic() == key; +bool MatchesMnemonic(MenuItemView* menu, base::char16 key) { + return key != 0 && menu->GetMnemonic() == key; } // Returns true if |menu| doesn't have a mnemonic and first character of the its // title is |key|. -bool TitleMatchesMnemonic(MenuItemView* menu, char16 key) { +bool TitleMatchesMnemonic(MenuItemView* menu, base::char16 key) { if (menu->GetMnemonic()) return false; - string16 lower_title = base::i18n::ToLower(menu->title()); + base::string16 lower_title = base::i18n::ToLower(menu->title()); return !lower_title.empty() && lower_title[0] == key; } @@ -279,8 +268,9 @@ struct MenuController::SelectByCharDetails { MenuController::State::State() : item(NULL), submenu_open(false), - anchor(MenuItemView::TOPLEFT), - context_menu(false) {} + anchor(MENU_ANCHOR_TOPLEFT), + context_menu(false) { +} MenuController::State::~State() {} @@ -298,7 +288,7 @@ MenuItemView* MenuController::Run(Widget* parent, MenuButton* button, MenuItemView* root, const gfx::Rect& bounds, - MenuItemView::AnchorPosition position, + MenuAnchorPosition position, bool context_menu, int* result_event_flags) { exit_type_ = EXIT_NONE; @@ -385,7 +375,7 @@ MenuItemView* MenuController::Run(Widget* parent, // Close any open menus. SetSelection(NULL, SELECTION_UPDATE_IMMEDIATELY | SELECTION_EXIT); -#if defined(OS_WIN) && defined(USE_AURA) +#if defined(OS_WIN) // On Windows, if we select the menu item by touch and if the window at the // location is another window on the same thread, that window gets a // WM_MOUSEACTIVATE message and ends up activating itself, which is not @@ -400,7 +390,7 @@ MenuItemView* MenuController::Run(Widget* parent, HWND window = ::WindowFromPoint(cursor_pos); if (::GetWindowThreadProcessId(window, NULL) == ::GetCurrentThreadId()) { - ::SetProp(window, views::kIgnoreTouchMouseActivateForWindow, + ::SetProp(window, ui::kIgnoreTouchMouseActivateForWindow, reinterpret_cast<HANDLE>(true)); } } @@ -596,13 +586,11 @@ void MenuController::OnMouseEntered(SubmenuView* source, // do anything here. } -#if defined(USE_AURA) bool MenuController::OnMouseWheel(SubmenuView* source, const ui::MouseWheelEvent& event) { MenuPart part = GetMenuPart(source, event.location()); return part.submenu && part.submenu->OnMouseWheel(event); } -#endif void MenuController::OnGestureEvent(SubmenuView* source, ui::GestureEvent* event) { @@ -809,6 +797,7 @@ void MenuController::OnWidgetDestroying(Widget* widget) { DCHECK_EQ(owner_, widget); owner_->RemoveObserver(this); owner_ = NULL; + message_loop_->ClearOwner(); } // static @@ -875,7 +864,7 @@ void MenuController::SetSelection(MenuItemView* menu_item, (MenuDepth(menu_item) != 1 || menu_item->GetType() != MenuItemView::SUBMENU)) { menu_item->NotifyAccessibilityEvent( - ui::AccessibilityTypes::EVENT_FOCUS, true); + ui::AX_EVENT_FOCUS, true); } } @@ -912,14 +901,7 @@ void MenuController::SetSelectionOnPointerDown(SubmenuView* source, #if defined(OS_WIN) // We're going to close and we own the mouse capture. We need to repost the // mouse down, otherwise the window the user clicked on won't get the event. - if (!state_.item) { - // We some times get an event after closing all the menus. Ignore it. Make - // sure the menu is in fact not visible. If the menu is visible, then - // we're in a bad state where we think the menu isn't visibile but it is. - DCHECK(!source->GetWidget()->IsVisible()); - } else { - RepostEvent(source, event); - } + RepostEvent(source, event); #endif // And close. @@ -936,14 +918,15 @@ void MenuController::SetSelectionOnPointerDown(SubmenuView* source, } Cancel(exit_type); -#if defined(USE_AURA) && !defined(OS_WIN) +#if defined(OS_CHROMEOS) // We're going to exit the menu and want to repost the event so that is // is handled normally after the context menu has exited. We call // RepostEvent after Cancel so that mouse capture has been released so // that finding the event target is unaffected by the current capture. RepostEvent(source, event); #endif - + // Do not repost events for Linux Aura because this behavior is more + // consistent with the behavior of other Linux apps. return; } @@ -982,7 +965,7 @@ void MenuController::StartDrag(SubmenuView* source, OSExchangeData data; item->GetDelegate()->WriteDragData(item, &data); - drag_utils::SetDragImageOnDataObject(*canvas, item->size(), + drag_utils::SetDragImageOnDataObject(*canvas, press_loc.OffsetFromOrigin(), &data); StopScrolling(); @@ -1002,94 +985,6 @@ void MenuController::StartDrag(SubmenuView* source, } // else case, someone canceled us, don't do anything } -#if defined(OS_WIN) -bool MenuController::Dispatch(const MSG& msg) { - DCHECK(blocking_run_); - - if (exit_type_ == EXIT_ALL || exit_type_ == EXIT_DESTROYED) { - // We must translate/dispatch the message here, otherwise we would drop - // the message on the floor. - TranslateMessage(&msg); - DispatchMessage(&msg); - return false; - } - - // NOTE: we don't get WM_ACTIVATE or anything else interesting in here. - switch (msg.message) { - case WM_CONTEXTMENU: { - MenuItemView* item = pending_state_.item; - if (item && item->GetRootMenuItem() != item) { - gfx::Point screen_loc(0, item->height()); - View::ConvertPointToScreen(item, &screen_loc); - ui::MenuSourceType source_type = ui::MENU_SOURCE_MOUSE; - if (GET_X_LPARAM(msg.lParam) == -1 && GET_Y_LPARAM(msg.lParam) == -1) - source_type = ui::MENU_SOURCE_KEYBOARD; - item->GetDelegate()->ShowContextMenu(item, item->GetCommand(), - screen_loc, source_type); - } - return true; - } - - // NOTE: focus wasn't changed when the menu was shown. As such, don't - // dispatch key events otherwise the focused window will get the events. - case WM_KEYDOWN: { - bool result = OnKeyDown(ui::KeyboardCodeFromNative(msg)); - TranslateMessage(&msg); - return result; - } - case WM_CHAR: - return !SelectByChar(static_cast<char16>(msg.wParam)); - case WM_KEYUP: - return true; - - case WM_SYSKEYUP: - // We may have been shown on a system key, as such don't do anything - // here. If another system key is pushed we'll get a WM_SYSKEYDOWN and - // close the menu. - return true; - - case WM_CANCELMODE: - case WM_SYSKEYDOWN: - // Exit immediately on system keys. - Cancel(EXIT_ALL); - return false; - - default: - break; - } - TranslateMessage(&msg); - DispatchMessage(&msg); - return exit_type_ == EXIT_NONE; -} -#elif defined(USE_AURA) -bool MenuController::Dispatch(const base::NativeEvent& event) { - if (exit_type_ == EXIT_ALL || exit_type_ == EXIT_DESTROYED) { - aura::Env::GetInstance()->GetDispatcher()->Dispatch(event); - return false; - } - // Activates mnemonics only when it it pressed without modifiers except for - // caps and shift. - int flags = ui::EventFlagsFromNative(event) & - ~ui::EF_CAPS_LOCK_DOWN & ~ui::EF_SHIFT_DOWN; - if (flags == ui::EF_NONE) { - switch (ui::EventTypeFromNative(event)) { - case ui::ET_KEY_PRESSED: - if (!OnKeyDown(ui::KeyboardCodeFromNative(event))) - return false; - - return !SelectByChar(ui::KeyboardCodeFromNative(event)); - case ui::ET_KEY_RELEASED: - return true; - default: - break; - } - } - - aura::Env::GetInstance()->GetDispatcher()->Dispatch(event); - return exit_type_ == EXIT_NONE; -} -#endif - bool MenuController::OnKeyDown(ui::KeyboardCode key_code) { DCHECK(blocking_run_); @@ -1124,7 +1019,7 @@ bool MenuController::OnKeyDown(ui::KeyboardCode key_code) { break; case ui::VKEY_F4: - if (!accept_on_f4_) + if (!is_combobox_) break; // Fallthrough to accept on F4, so combobox menus match Windows behavior. case ui::VKEY_RETURN: @@ -1156,11 +1051,6 @@ bool MenuController::OnKeyDown(ui::KeyboardCode key_code) { CloseSubmenu(); break; -#if defined(OS_WIN) - case VK_APPS: - break; -#endif - default: break; } @@ -1191,8 +1081,9 @@ MenuController::MenuController(ui::NativeTheme* theme, menu_config_(theme), closing_event_time_(base::TimeDelta()), menu_start_time_(base::TimeTicks()), - accept_on_f4_(false), - item_selected_by_touch_(false) { + is_combobox_(false), + item_selected_by_touch_(false), + message_loop_(MenuMessageLoop::Create()) { active_instance_ = this; } @@ -1206,6 +1097,10 @@ MenuController::~MenuController() { StopCancelAllTimer(); } +void MenuController::RunMessageLoop(bool nested_menu) { + message_loop_->Run(this, owner_, nested_menu); +} + MenuController::SendAcceleratorResultType MenuController::SendAcceleratorToHotTrackedView() { CustomButton* hot_view = GetFirstHotTrackedView(pending_state_.item); @@ -1220,10 +1115,9 @@ MenuController::SendAcceleratorResultType ACCELERATOR_PROCESSED : ACCELERATOR_PROCESSED_EXIT; } -void MenuController::UpdateInitialLocation( - const gfx::Rect& bounds, - MenuItemView::AnchorPosition position, - bool context_menu) { +void MenuController::UpdateInitialLocation(const gfx::Rect& bounds, + MenuAnchorPosition position, + bool context_menu) { pending_state_.context_menu = context_menu; pending_state_.initial_bounds = bounds; if (bounds.height() > 1) { @@ -1234,10 +1128,10 @@ void MenuController::UpdateInitialLocation( // Reverse anchor position for RTL languages. if (base::i18n::IsRTL() && - (position == MenuItemView::TOPRIGHT || - position == MenuItemView::TOPLEFT)) { - pending_state_.anchor = position == MenuItemView::TOPRIGHT ? - MenuItemView::TOPLEFT : MenuItemView::TOPRIGHT; + (position == MENU_ANCHOR_TOPRIGHT || position == MENU_ANCHOR_TOPLEFT)) { + pending_state_.anchor = position == MENU_ANCHOR_TOPRIGHT + ? MENU_ANCHOR_TOPLEFT + : MENU_ANCHOR_TOPRIGHT; } else { pending_state_.anchor = position; } @@ -1246,7 +1140,7 @@ void MenuController::UpdateInitialLocation( // avoid repeated system queries for the info. pending_state_.monitor_bounds = GetScreen()->GetDisplayNearestPoint( bounds.origin()).work_area(); -#if defined(USE_ASH) + if (!pending_state_.monitor_bounds.Contains(bounds)) { // Use the monitor area if the work area doesn't contain the bounds. This // handles showing a menu from the launcher. @@ -1255,7 +1149,6 @@ void MenuController::UpdateInitialLocation( if (monitor_area.Contains(bounds)) pending_state_.monitor_bounds = monitor_area; } -#endif } void MenuController::Accept(MenuItemView* item, int event_flags) { @@ -1293,7 +1186,7 @@ bool MenuController::ShowSiblingMenu(SubmenuView* source, // if there is a sibling menu we should show. gfx::Point screen_point(mouse_location); View::ConvertPointToScreen(source_view, &screen_point); - MenuItemView::AnchorPosition anchor; + MenuAnchorPosition anchor; bool has_mnemonics; MenuButton* button = NULL; MenuItemView* alt_menu = source->GetMenuItem()->GetDelegate()-> @@ -1717,11 +1610,11 @@ gfx::Rect MenuController::CalculateMenuBounds(MenuItemView* item, x += 1; y = state_.initial_bounds.bottom(); - if (state_.anchor == MenuItemView::TOPRIGHT) { + if (state_.anchor == MENU_ANCHOR_TOPRIGHT) { x = x + state_.initial_bounds.width() - pref.width(); if (menu_config.offset_context_menus && state_.context_menu) x -= 1; - } else if (state_.anchor == MenuItemView::BOTTOMCENTER) { + } else if (state_.anchor == MENU_ANCHOR_BOTTOMCENTER) { x = x - (pref.width() - state_.initial_bounds.width()) / 2; if (pref.height() > state_.initial_bounds.y() + kCenteredContextMenuYOffset) { @@ -1770,7 +1663,7 @@ gfx::Rect MenuController::CalculateMenuBounds(MenuItemView* item, // The menu should never overlap the owning button. So move it. // We use the anchor view style to determine the preferred position // relative to the owning button. - if (state_.anchor == MenuItemView::TOPLEFT) { + if (state_.anchor == MENU_ANCHOR_TOPLEFT) { // The menu starts with the same x coordinate as the owning button. if (x + state_.initial_bounds.width() + pref.width() > state_.monitor_bounds.right()) @@ -1883,16 +1776,16 @@ gfx::Rect MenuController::CalculateBubbleMenuBounds(MenuItemView* item, int max_height = state_.monitor_bounds.height(); // In case of bubbles, the maximum width is limited by the space // between the display corner and the target area + the tip size. - if (state_.anchor == MenuItemView::BUBBLE_LEFT) { + if (state_.anchor == MENU_ANCHOR_BUBBLE_LEFT) { max_width = owner_bounds.x() - state_.monitor_bounds.x() + kBubbleTipSizeLeftRight; - } else if (state_.anchor == MenuItemView::BUBBLE_RIGHT) { + } else if (state_.anchor == MENU_ANCHOR_BUBBLE_RIGHT) { max_width = state_.monitor_bounds.right() - owner_bounds.right() + kBubbleTipSizeLeftRight; - } else if (state_.anchor == MenuItemView::BUBBLE_ABOVE) { + } else if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE) { max_height = owner_bounds.y() - state_.monitor_bounds.y() + kBubbleTipSizeTopBottom; - } else if (state_.anchor == MenuItemView::BUBBLE_BELOW) { + } else if (state_.anchor == MENU_ANCHOR_BUBBLE_BELOW) { max_height = state_.monitor_bounds.bottom() - owner_bounds.bottom() + kBubbleTipSizeTopBottom; } @@ -1907,9 +1800,9 @@ gfx::Rect MenuController::CalculateBubbleMenuBounds(MenuItemView* item, item->GetDelegate()->GetMaxWidthForMenu(item))); int x, y; - if (state_.anchor == MenuItemView::BUBBLE_ABOVE || - state_.anchor == MenuItemView::BUBBLE_BELOW) { - if (state_.anchor == MenuItemView::BUBBLE_ABOVE) + if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE || + state_.anchor == MENU_ANCHOR_BUBBLE_BELOW) { + if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE) y = owner_bounds.y() - pref.height() + kBubbleTipSizeTopBottom; else y = owner_bounds.bottom() - kBubbleTipSizeTopBottom; @@ -1924,7 +1817,7 @@ gfx::Rect MenuController::CalculateBubbleMenuBounds(MenuItemView* item, submenu->GetScrollViewContainer()->SetBubbleArrowOffset( pref.width() / 2 - x + x_old); } else { - if (state_.anchor == MenuItemView::BUBBLE_RIGHT) + if (state_.anchor == MENU_ANCHOR_BUBBLE_RIGHT) x = owner_bounds.right() - kBubbleTipSizeLeftRight; else x = owner_bounds.x() - pref.width() + kBubbleTipSizeLeftRight; @@ -2046,8 +1939,8 @@ void MenuController::CloseSubmenu() { MenuController::SelectByCharDetails MenuController::FindChildForMnemonic( MenuItemView* parent, - char16 key, - bool (*match_function)(MenuItemView* menu, char16 mnemonic)) { + base::char16 key, + bool (*match_function)(MenuItemView* menu, base::char16 mnemonic)) { SubmenuView* submenu = parent->GetSubmenu(); DCHECK(submenu); SelectByCharDetails details; @@ -2098,9 +1991,9 @@ bool MenuController::AcceptOrSelect(MenuItemView* parent, return false; } -bool MenuController::SelectByChar(char16 character) { - char16 char_array[] = { character, 0 }; - char16 key = base::i18n::ToLower(char_array)[0]; +bool MenuController::SelectByChar(base::char16 character) { + base::char16 char_array[] = { character, 0 }; + base::char16 key = base::i18n::ToLower(char_array)[0]; MenuItemView* item = pending_state_.item; if (!item->HasSubmenu() || !item->GetSubmenu()->IsShowing()) item = item->GetParentMenuItem(); @@ -2116,61 +2009,114 @@ bool MenuController::SelectByChar(char16 character) { if (details.first_match != -1) return AcceptOrSelect(item, details); - // If no mnemonics found, look at first character of titles. - details = FindChildForMnemonic(item, key, &TitleMatchesMnemonic); - if (details.first_match != -1) - return AcceptOrSelect(item, details); + if (is_combobox_) { + item->GetSubmenu()->GetTextInputClient()->InsertChar(character, 0); + } else { + // If no mnemonics found, look at first character of titles. + details = FindChildForMnemonic(item, key, &TitleMatchesMnemonic); + if (details.first_match != -1) + return AcceptOrSelect(item, details); + } return false; } void MenuController::RepostEvent(SubmenuView* source, const ui::LocatedEvent& event) { + if (!event.IsMouseEvent()) { + // TODO(rbyers): Gesture event repost is tricky to get right + // crbug.com/170987. + DCHECK(event.IsGestureEvent()); + return; + } + +#if defined(OS_WIN) + if (!state_.item) { + // We some times get an event after closing all the menus. Ignore it. Make + // sure the menu is in fact not visible. If the menu is visible, then + // we're in a bad state where we think the menu isn't visibile but it is. + DCHECK(!source->GetWidget()->IsVisible()); + return; + } + + state_.item->GetRootMenuItem()->GetSubmenu()->ReleaseCapture(); +#endif + gfx::Point screen_loc(event.location()); View::ConvertPointToScreen(source->GetScrollViewContainer(), &screen_loc); - gfx::NativeView native_view = source->GetWidget()->GetNativeView(); + if (!native_view) + return; + gfx::Screen* screen = gfx::Screen::GetScreenFor(native_view); gfx::NativeWindow window = screen->GetWindowAtScreenPoint(screen_loc); - if (!window) - return; - #if defined(OS_WIN) - // Release the capture. - SubmenuView* submenu = state_.item->GetRootMenuItem()->GetSubmenu(); - submenu->ReleaseCapture(); - - gfx::NativeView view = submenu->GetWidget()->GetNativeView(); - if (view) { - DWORD view_tid = GetWindowThreadProcessId(HWNDForNativeView(view), NULL); - if (view_tid != GetWindowThreadProcessId(HWNDForNativeView(window), NULL)) { + // Convert screen_loc to pixels for the Win32 API's like WindowFromPoint, + // PostMessage/SendMessage to work correctly. These API's expect the + // coordinates to be in pixels. + // PostMessage() to metro windows isn't allowed (access will be denied). Don't + // try to repost with Win32 if the window under the mouse press is in metro. + if (!ViewsDelegate::views_delegate || + !ViewsDelegate::views_delegate->IsWindowInMetro(window)) { + gfx::Point screen_loc_pixels = gfx::win::DIPToScreenPoint(screen_loc); + HWND target_window = window ? HWNDForNativeWindow(window) : + WindowFromPoint(screen_loc_pixels.ToPOINT()); + HWND source_window = HWNDForNativeView(native_view); + if (!target_window || !source_window || + GetWindowThreadProcessId(source_window, NULL) != + GetWindowThreadProcessId(target_window, NULL)) { // Even though we have mouse capture, windows generates a mouse event if // the other window is in a separate thread. Only repost an event if - // |view| was created on the same thread, else the target window can get - // double events leading to bad behavior. + // |target_window| and |source_window| were created on the same thread, + // else double events can occur and lead to bad behavior. return; } - } -#endif - scoped_ptr<ui::LocatedEvent> clone; - if (event.IsMouseEvent()) { - clone.reset(new ui::MouseEvent(static_cast<const ui::MouseEvent&>(event))); - } else if (event.IsGestureEvent()) { - // TODO(rbyers): Gesture event repost is tricky to get right - // crbug.com/170987. - return; - } else { - NOTREACHED(); + // Determine whether the click was in the client area or not. + // NOTE: WM_NCHITTEST coordinates are relative to the screen. + LPARAM coords = MAKELPARAM(screen_loc_pixels.x(), screen_loc_pixels.y()); + LRESULT nc_hit_result = SendMessage(target_window, WM_NCHITTEST, 0, coords); + const bool client_area = nc_hit_result == HTCLIENT; + + // TODO(sky): this isn't right. The event to generate should correspond with + // the event we just got. MouseEvent only tells us what is down, which may + // differ. Need to add ability to get changed button from MouseEvent. + int event_type; + int flags = event.flags(); + if (flags & ui::EF_LEFT_MOUSE_BUTTON) { + event_type = client_area ? WM_LBUTTONDOWN : WM_NCLBUTTONDOWN; + } else if (flags & ui::EF_MIDDLE_MOUSE_BUTTON) { + event_type = client_area ? WM_MBUTTONDOWN : WM_NCMBUTTONDOWN; + } else if (flags & ui::EF_RIGHT_MOUSE_BUTTON) { + event_type = client_area ? WM_RBUTTONDOWN : WM_NCRBUTTONDOWN; + } else { + NOTREACHED(); + return; + } + + int window_x = screen_loc_pixels.x(); + int window_y = screen_loc_pixels.y(); + if (client_area) { + POINT pt = { window_x, window_y }; + ScreenToClient(target_window, &pt); + window_x = pt.x; + window_y = pt.y; + } + + WPARAM target = client_area ? event.native_event().wParam : nc_hit_result; + LPARAM window_coords = MAKELPARAM(window_x, window_y); + PostMessage(target_window, event_type, target, window_coords); return; } - clone->set_location(screen_loc); +#endif + // Non-Windows Aura or |window| is in metro mode. + if (!window) + return; - RepostLocatedEvent(window, *clone); + message_loop_->RepostEventToWindow(event, window, screen_loc); } - void MenuController::SetDropMenuItem( MenuItemView* new_target, MenuDelegate::DropPosition new_position) { @@ -2230,12 +2176,13 @@ void MenuController::UpdateActiveMouseView(SubmenuView* event_source, target_menu, active_mouse_view, &target_point); ui::MouseEvent mouse_entered_event(ui::ET_MOUSE_ENTERED, target_point, target_point, - 0); + 0, 0); active_mouse_view->OnMouseEntered(mouse_entered_event); ui::MouseEvent mouse_pressed_event(ui::ET_MOUSE_PRESSED, target_point, target_point, - event.flags()); + event.flags(), + event.changed_button_flags()); active_mouse_view->OnMousePressed(mouse_pressed_event); } } @@ -2245,7 +2192,8 @@ void MenuController::UpdateActiveMouseView(SubmenuView* event_source, View::ConvertPointToTarget(target_menu, active_mouse_view, &target_point); ui::MouseEvent mouse_dragged_event(ui::ET_MOUSE_DRAGGED, target_point, target_point, - event.flags()); + event.flags(), + event.changed_button_flags()); active_mouse_view->OnMouseDragged(mouse_dragged_event); } } @@ -2261,7 +2209,7 @@ void MenuController::SendMouseReleaseToActiveView(SubmenuView* event_source, &target_loc); View::ConvertPointFromScreen(active_mouse_view, &target_loc); ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, target_loc, target_loc, - event.flags()); + event.flags(), event.changed_button_flags()); // Reset active mouse view before sending mouse released. That way if it calls // back to us, we aren't in a weird state. SetActiveMouseView(NULL); @@ -2293,20 +2241,22 @@ View* MenuController::GetActiveMouseView() { void MenuController::SetExitType(ExitType type) { exit_type_ = type; // Exit nested message loops as soon as possible. We do this as - // MessageLoop::Dispatcher is only invoked before native events, which means + // MessagePumpDispatcher is only invoked before native events, which means // its entirely possible for a Widget::CloseNow() task to be processed before - // the next native message. By using QuitNow() we ensures the nested message - // loop returns as soon as possible and avoids having deleted views classes - // (such as widgets and rootviews) on the stack when the nested message loop - // stops. + // the next native message. We quite the nested message loop as soon as + // possible to avoid having deleted views classes (such as widgets and + // rootviews) on the stack when the nested message loop stops. // - // It's safe to invoke QuitNow multiple times, it only effects the current - // loop. - bool quit_now = ShouldQuitNow() && exit_type_ != EXIT_NONE && + // It's safe to invoke QuitNestedMessageLoop() multiple times, it only effects + // the current loop. + bool quit_now = message_loop_->ShouldQuitNow() && exit_type_ != EXIT_NONE && message_loop_depth_; - if (quit_now) - base::MessageLoop::current()->QuitNow(); + TerminateNestedMessageLoop(); +} + +void MenuController::TerminateNestedMessageLoop() { + message_loop_->QuitNow(); } void MenuController::HandleMouseLocation(SubmenuView* source, @@ -2341,4 +2291,10 @@ void MenuController::HandleMouseLocation(SubmenuView* source, } } +gfx::Screen* MenuController::GetScreen() { + Widget* root = owner_ ? owner_->GetTopLevelWidget() : NULL; + return root ? gfx::Screen::GetScreenFor(root->GetNativeView()) + : gfx::Screen::GetNativeScreen(); +} + } // namespace views diff --git a/chromium/ui/views/controls/menu/menu_controller.h b/chromium/ui/views/controls/menu/menu_controller.h index 1404d54e5d6..30c56f0f04b 100644 --- a/chromium/ui/views/controls/menu/menu_controller.h +++ b/chromium/ui/views/controls/menu/menu_controller.h @@ -13,30 +13,39 @@ #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" #include "base/timer/timer.h" +#include "ui/events/event.h" #include "ui/events/event_constants.h" +#include "ui/events/platform/platform_event_dispatcher.h" +#include "ui/views/controls/menu/menu_config.h" #include "ui/views/controls/menu/menu_delegate.h" -#include "ui/views/controls/menu/menu_item_view.h" #include "ui/views/widget/widget_observer.h" -namespace ui { -class NativeTheme; -class OSExchangeData; +namespace base { +class MessagePumpDispatcher; } namespace gfx { class Screen; } +namespace ui { +class NativeTheme; +class OSExchangeData; +class ScopedEventDispatcher; +} namespace views { class MenuButton; class MenuHostRootView; +class MenuItemView; +class MenuMessageLoop; class MouseEvent; class SubmenuView; class View; namespace internal { class MenuControllerDelegate; +class MenuEventDispatcher; +class MenuMessagePumpDispatcher; class MenuRunnerImpl; } @@ -45,8 +54,7 @@ class MenuRunnerImpl; // MenuController is used internally by the various menu classes to manage // showing, selecting and drag/drop for menus. All relevant events are // forwarded to the MenuController from SubmenuView and MenuHost. -class VIEWS_EXPORT MenuController : public base::MessageLoop::Dispatcher, - public WidgetObserver { +class VIEWS_EXPORT MenuController : public WidgetObserver { public: // Enumeration of how the menu should exit. enum ExitType { @@ -74,7 +82,7 @@ class VIEWS_EXPORT MenuController : public base::MessageLoop::Dispatcher, MenuButton* button, MenuItemView* root, const gfx::Rect& bounds, - MenuItemView::AnchorPosition position, + MenuAnchorPosition position, bool context_menu, int* event_flags); @@ -84,8 +92,12 @@ class VIEWS_EXPORT MenuController : public base::MessageLoop::Dispatcher, // Whether or not drag operation is in progress. bool drag_in_progress() const { return drag_in_progress_; } + // Returns the owner of child windows. + // WARNING: this may be NULL. + Widget* owner() { return owner_; } + // Get the anchor position wich is used to show this menu. - MenuItemView::AnchorPosition GetAnchorPosition() { return state_.anchor; } + MenuAnchorPosition GetAnchorPosition() { return state_.anchor; } // Cancels the current Run. See ExitType for a description of what happens // with the various parameters. @@ -101,7 +113,7 @@ class VIEWS_EXPORT MenuController : public base::MessageLoop::Dispatcher, // Returns the time from the event which closed the menu - or 0. base::TimeDelta closing_event_time() const { return closing_event_time_; } - void set_accept_on_f4(bool accept_on_f4) { accept_on_f4_ = accept_on_f4; } + void set_is_combobox(bool is_combobox) { is_combobox_ = is_combobox; } // Various events, forwarded from the submenu. // @@ -112,9 +124,7 @@ class VIEWS_EXPORT MenuController : public base::MessageLoop::Dispatcher, void OnMouseReleased(SubmenuView* source, const ui::MouseEvent& event); void OnMouseMoved(SubmenuView* source, const ui::MouseEvent& event); void OnMouseEntered(SubmenuView* source, const ui::MouseEvent& event); -#if defined(USE_AURA) bool OnMouseWheel(SubmenuView* source, const ui::MouseWheelEvent& event); -#endif void OnGestureEvent(SubmenuView* source, ui::GestureEvent* event); bool GetDropFormats( @@ -142,7 +152,10 @@ class VIEWS_EXPORT MenuController : public base::MessageLoop::Dispatcher, static void TurnOffMenuSelectionHoldForTest(); private: + friend class internal::MenuEventDispatcher; + friend class internal::MenuMessagePumpDispatcher; friend class internal::MenuRunnerImpl; + friend class MenuControllerTest; friend class MenuHostRootView; friend class MenuItemView; friend class SubmenuView; @@ -194,7 +207,7 @@ class VIEWS_EXPORT MenuController : public base::MessageLoop::Dispatcher, gfx::Rect initial_bounds; // Position of the initial menu. - MenuItemView::AnchorPosition anchor; + MenuAnchorPosition anchor; // The direction child menus have opened in. std::list<bool> open_leading; @@ -253,10 +266,6 @@ class VIEWS_EXPORT MenuController : public base::MessageLoop::Dispatcher, const ui::LocatedEvent& event); void StartDrag(SubmenuView* source, const gfx::Point& location); - // Dispatcher method. This returns true if the menu was canceled, or - // if the message is such that the menu should be closed. - virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE; - // Key processing. The return value of this is returned from Dispatch. // In other words, if this returns false (which happens if escape was // pressed, or a matching mnemonic was found) the message loop returns. @@ -279,7 +288,7 @@ class VIEWS_EXPORT MenuController : public base::MessageLoop::Dispatcher, SendAcceleratorResultType SendAcceleratorToHotTrackedView(); void UpdateInitialLocation(const gfx::Rect& bounds, - MenuItemView::AnchorPosition position, + MenuAnchorPosition position, bool context_menu); // Invoked when the user accepts the selected item. This is only used @@ -416,8 +425,8 @@ class VIEWS_EXPORT MenuController : public base::MessageLoop::Dispatcher, // |match_function| is used to determine which menus match. SelectByCharDetails FindChildForMnemonic( MenuItemView* parent, - char16 key, - bool (*match_function)(MenuItemView* menu, char16 mnemonic)); + base::char16 key, + bool (*match_function)(MenuItemView* menu, base::char16 mnemonic)); // Selects or accepts the appropriate menu item based on |details|. Returns // true if |Accept| was invoked (which happens if there aren't multiple item @@ -426,7 +435,7 @@ class VIEWS_EXPORT MenuController : public base::MessageLoop::Dispatcher, // Selects by mnemonic, and if that doesn't work tries the first character of // the title. Returns true if a match was selected and the menu should exit. - bool SelectByChar(char16 key); + bool SelectByChar(base::char16 key); // For Windows and Aura we repost an event for some events that dismiss // the context menu. The event is then reprocessed to cause its result @@ -468,9 +477,12 @@ class VIEWS_EXPORT MenuController : public base::MessageLoop::Dispatcher, void SetActiveMouseView(View* view); View* GetActiveMouseView(); - // Sets exit type. + // Sets exit type. Calling this can terminate the active nested message-loop. void SetExitType(ExitType type); + // Terminates the current nested message-loop. + void TerminateNestedMessageLoop(); + // Returns true if SetExitType() should quit the message loop. bool ShouldQuitNow() const; @@ -584,12 +596,15 @@ class VIEWS_EXPORT MenuController : public base::MessageLoop::Dispatcher, // screen coordinates). Otherwise this will be (0, 0). gfx::Point menu_start_mouse_press_loc_; - // Whether the menu should accept on F4, like Windows native Combobox menus. - bool accept_on_f4_; + // Controls behavior differences between a combobox and other types of menu + // (like a context menu). + bool is_combobox_; // Set to true if the menu item was selected by touch. bool item_selected_by_touch_; + scoped_ptr<MenuMessageLoop> message_loop_; + DISALLOW_COPY_AND_ASSIGN(MenuController); }; diff --git a/chromium/ui/views/controls/menu/menu_controller_aura.cc b/chromium/ui/views/controls/menu/menu_controller_aura.cc deleted file mode 100644 index 48bf3118ff6..00000000000 --- a/chromium/ui/views/controls/menu/menu_controller_aura.cc +++ /dev/null @@ -1,115 +0,0 @@ -// 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/controls/menu/menu_controller.h" - -#include "base/run_loop.h" -#include "ui/aura/client/activation_change_observer.h" -#include "ui/aura/client/activation_client.h" -#include "ui/aura/client/dispatcher_client.h" -#include "ui/aura/client/drag_drop_client.h" -#include "ui/aura/window.h" -#include "ui/aura/window_observer.h" -#include "ui/gfx/screen.h" -#include "ui/views/widget/widget.h" - -namespace views { - -namespace { - -// ActivationChangeObserverImpl is used to observe activation changes and close -// the menu. Additionally it listens for the root window to be destroyed and -// cancel the menu as well. -class ActivationChangeObserverImpl - : public aura::client::ActivationChangeObserver, - public aura::WindowObserver, - public ui::EventHandler { - public: - ActivationChangeObserverImpl(MenuController* controller, - aura::Window* root) - : controller_(controller), - root_(root) { - aura::client::GetActivationClient(root_)->AddObserver(this); - root_->AddObserver(this); - root_->AddPreTargetHandler(this); - } - - virtual ~ActivationChangeObserverImpl() { - Cleanup(); - } - - // aura::client::ActivationChangeObserver overrides: - virtual void OnWindowActivated(aura::Window* gained_active, - aura::Window* lost_active) OVERRIDE { - if (!controller_->drag_in_progress()) - controller_->CancelAll(); - } - - // aura::WindowObserver overrides: - virtual void OnWindowDestroying(aura::Window* window) OVERRIDE { - Cleanup(); - } - - // ui::EventHandler overrides: - virtual void OnCancelMode(ui::CancelModeEvent* event) OVERRIDE { - controller_->CancelAll(); - } - - private: - void Cleanup() { - if (!root_) - return; - // The ActivationClient may have been destroyed by the time we get here. - aura::client::ActivationClient* client = - aura::client::GetActivationClient(root_); - if (client) - client->RemoveObserver(this); - root_->RemovePreTargetHandler(this); - root_->RemoveObserver(this); - root_ = NULL; - } - - MenuController* controller_; - aura::Window* root_; - - DISALLOW_COPY_AND_ASSIGN(ActivationChangeObserverImpl); -}; - -aura::Window* GetOwnerRootWindow(views::Widget* owner) { - return owner ? owner->GetNativeWindow()->GetRootWindow() : NULL; -} - -} // namespace - -void MenuController::RunMessageLoop(bool nested_menu) { - // |owner_| may be NULL. - aura::Window* root = GetOwnerRootWindow(owner_); - if (root) { - scoped_ptr<ActivationChangeObserverImpl> observer; - if (!nested_menu) - observer.reset(new ActivationChangeObserverImpl(this, root)); - aura::client::GetDispatcherClient(root)-> - RunWithDispatcher(this, owner_->GetNativeWindow(), true); - } else { - base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); - base::MessageLoop::ScopedNestableTaskAllower allow(loop); - base::RunLoop run_loop(this); - run_loop.Run(); - } -} - -bool MenuController::ShouldQuitNow() const { - aura::Window* root = GetOwnerRootWindow(owner_); - return !aura::client::GetDragDropClient(root) || - !aura::client::GetDragDropClient(root)->IsDragDropInProgress(); -} - -gfx::Screen* MenuController::GetScreen() { - aura::Window* root = GetOwnerRootWindow(owner_); - return root ? - gfx::Screen::GetScreenFor(root) : gfx::Screen::GetNativeScreen(); -} - - -} // namespace views diff --git a/chromium/ui/views/controls/menu/menu_controller_unittest.cc b/chromium/ui/views/controls/menu/menu_controller_unittest.cc new file mode 100644 index 00000000000..1c98f95bc9c --- /dev/null +++ b/chromium/ui/views/controls/menu/menu_controller_unittest.cc @@ -0,0 +1,267 @@ +// Copyright 2014 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/controls/menu/menu_controller.h" + +#include "base/run_loop.h" +#include "ui/aura/scoped_window_targeter.h" +#include "ui/aura/window.h" +#include "ui/events/event_targeter.h" +#include "ui/events/platform/platform_event_source.h" +#include "ui/views/controls/menu/menu_item_view.h" +#include "ui/views/test/views_test_base.h" +#include "ui/wm/public/dispatcher_client.h" + +#if defined(OS_WIN) +#include "base/message_loop/message_pump_dispatcher.h" +#elif defined(USE_X11) +#include <X11/Xlib.h> +#undef Bool +#undef None +#include "ui/events/test/events_test_utils_x11.h" +#elif defined(USE_OZONE) +#include "ui/events/event.h" +#endif + +namespace views { + +namespace { + +class TestMenuItemView : public MenuItemView { + public: + TestMenuItemView() : MenuItemView(NULL) {} + virtual ~TestMenuItemView() {} + + private: + DISALLOW_COPY_AND_ASSIGN(TestMenuItemView); +}; + +class TestPlatformEventSource : public ui::PlatformEventSource { + public: + TestPlatformEventSource() {} + virtual ~TestPlatformEventSource() {} + + uint32_t Dispatch(const ui::PlatformEvent& event) { + return DispatchEvent(event); + } + + private: + DISALLOW_COPY_AND_ASSIGN(TestPlatformEventSource); +}; + +class TestNullTargeter : public ui::EventTargeter { + public: + TestNullTargeter() {} + virtual ~TestNullTargeter() {} + + virtual ui::EventTarget* FindTargetForEvent(ui::EventTarget* root, + ui::Event* event) OVERRIDE { + return NULL; + } + + private: + DISALLOW_COPY_AND_ASSIGN(TestNullTargeter); +}; + +class TestDispatcherClient : public aura::client::DispatcherClient { + public: + TestDispatcherClient() : dispatcher_(NULL) {} + virtual ~TestDispatcherClient() {} + + base::MessagePumpDispatcher* dispatcher() { + return dispatcher_; + } + + // aura::client::DispatcherClient: + virtual void PrepareNestedLoopClosures( + base::MessagePumpDispatcher* dispatcher, + base::Closure* run_closure, + base::Closure* quit_closure) OVERRIDE { + scoped_ptr<base::RunLoop> run_loop(new base::RunLoop()); + *quit_closure = run_loop->QuitClosure(); + *run_closure = base::Bind(&TestDispatcherClient::RunNestedDispatcher, + base::Unretained(this), + base::Passed(&run_loop), + dispatcher); + } + + private: + void RunNestedDispatcher(scoped_ptr<base::RunLoop> run_loop, + base::MessagePumpDispatcher* dispatcher) { + base::AutoReset<base::MessagePumpDispatcher*> reset_dispatcher(&dispatcher_, + dispatcher); + base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); + base::MessageLoop::ScopedNestableTaskAllower allow(loop); + run_loop->Run(); + } + + base::MessagePumpDispatcher* dispatcher_; + + DISALLOW_COPY_AND_ASSIGN(TestDispatcherClient); +}; + +} // namespace + +class MenuControllerTest : public ViewsTestBase { + public: + MenuControllerTest() : controller_(NULL) {} + virtual ~MenuControllerTest() { + ResetMenuController(); + } + + // Dispatches |count| number of items, each in a separate iteration of the + // message-loop, by posting a task. + void Step3_DispatchEvents(int count) { + base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); + base::MessageLoop::ScopedNestableTaskAllower allow(loop); + controller_->exit_type_ = MenuController::EXIT_ALL; + + DispatchEvent(); + if (count) { + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&MenuControllerTest::Step3_DispatchEvents, + base::Unretained(this), + count - 1)); + } else { + EXPECT_TRUE(run_loop_->running()); + run_loop_->Quit(); + } + } + + // Runs a nested message-loop that does not involve the menu itself. + void Step2_RunNestedLoop() { + base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); + base::MessageLoop::ScopedNestableTaskAllower allow(loop); + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&MenuControllerTest::Step3_DispatchEvents, + base::Unretained(this), + 3)); + run_loop_.reset(new base::RunLoop()); + run_loop_->Run(); + } + + void Step1_RunMenu() { + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&MenuControllerTest::Step2_RunNestedLoop, + base::Unretained(this))); + scoped_ptr<Widget> owner(CreateOwnerWidget()); + RunMenu(owner.get()); + } + + scoped_ptr<Widget> CreateOwnerWidget() { + scoped_ptr<Widget> widget(new Widget); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget->Init(params); + widget->Show(); + + aura::client::SetDispatcherClient( + widget->GetNativeWindow()->GetRootWindow(), &dispatcher_client_); + return widget.Pass(); + } + + void RunMenu(views::Widget* owner) { + scoped_ptr<TestMenuItemView> menu_item(new TestMenuItemView); + ResetMenuController(); + controller_ = new MenuController(NULL, true, NULL); + controller_->owner_ = owner; + controller_->showing_ = true; + controller_->SetSelection(menu_item.get(), + MenuController::SELECTION_UPDATE_IMMEDIATELY); + controller_->RunMessageLoop(false); + } + +#if defined(USE_X11) + void DispatchEscapeAndExpect(MenuController::ExitType exit_type) { + ui::ScopedXI2Event key_event; + key_event.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_ESCAPE, 0); + event_source_.Dispatch(key_event); + EXPECT_EQ(exit_type, controller_->exit_type()); + controller_->exit_type_ = MenuController::EXIT_ALL; + DispatchEvent(); + } +#endif + + void DispatchEvent() { +#if defined(USE_X11) + XEvent xevent; + memset(&xevent, 0, sizeof(xevent)); + event_source_.Dispatch(&xevent); +#elif defined(OS_WIN) + MSG msg; + memset(&msg, 0, sizeof(MSG)); + dispatcher_client_.dispatcher()->Dispatch(msg); +#elif defined(USE_OZONE) + ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_SPACE, 0, true); + dispatcher_client_.dispatcher()->Dispatch(&event); +#else +#error Unsupported platform +#endif + } + + private: + void ResetMenuController() { + if (controller_) { + // These properties are faked by RunMenu for the purposes of testing and + // need to be undone before we call the destructor. + controller_->owner_ = NULL; + controller_->showing_ = false; + delete controller_; + controller_ = NULL; + } + } + + // A weak pointer to the MenuController owned by this class. + MenuController* controller_; + scoped_ptr<base::RunLoop> run_loop_; + TestPlatformEventSource event_source_; + TestDispatcherClient dispatcher_client_; + + DISALLOW_COPY_AND_ASSIGN(MenuControllerTest); +}; + +TEST_F(MenuControllerTest, Basic) { + base::MessageLoop::ScopedNestableTaskAllower allow_nested( + base::MessageLoop::current()); + message_loop()->PostTask( + FROM_HERE, + base::Bind(&MenuControllerTest::Step1_RunMenu, base::Unretained(this))); +} + +#if defined(OS_LINUX) && defined(USE_X11) +// Tests that an event targeter which blocks events will be honored by the menu +// event dispatcher. +TEST_F(MenuControllerTest, EventTargeter) { + { + // Verify that the menu handles the escape key under normal circumstances. + scoped_ptr<Widget> owner(CreateOwnerWidget()); + message_loop()->PostTask( + FROM_HERE, + base::Bind(&MenuControllerTest::DispatchEscapeAndExpect, + base::Unretained(this), + MenuController::EXIT_OUTERMOST)); + RunMenu(owner.get()); + } + + { + // With the NULL targeter instantiated and assigned we expect the menu to + // not handle the key event. + scoped_ptr<Widget> owner(CreateOwnerWidget()); + aura::ScopedWindowTargeter scoped_targeter( + owner->GetNativeWindow()->GetRootWindow(), + scoped_ptr<ui::EventTargeter>(new TestNullTargeter)); + message_loop()->PostTask( + FROM_HERE, + base::Bind(&MenuControllerTest::DispatchEscapeAndExpect, + base::Unretained(this), + MenuController::EXIT_NONE)); + RunMenu(owner.get()); + } +} +#endif + +} // namespace views diff --git a/chromium/ui/views/controls/menu/menu_controller_win.cc b/chromium/ui/views/controls/menu/menu_controller_win.cc deleted file mode 100644 index bbf7e9b1366..00000000000 --- a/chromium/ui/views/controls/menu/menu_controller_win.cc +++ /dev/null @@ -1,27 +0,0 @@ -// 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/controls/menu/menu_controller.h" - -#include "base/run_loop.h" -#include "ui/gfx/screen.h" - -namespace views { - -void MenuController::RunMessageLoop(bool nested_menu) { - base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); - base::MessageLoop::ScopedNestableTaskAllower allow(loop); - base::RunLoop run_loop(this); - run_loop.Run(); -} - -bool MenuController::ShouldQuitNow() const { - return true; -} - -gfx::Screen* MenuController::GetScreen() { - return gfx::Screen::GetNativeScreen(); -} - -} // namespace views diff --git a/chromium/ui/views/controls/menu/menu_delegate.cc b/chromium/ui/views/controls/menu/menu_delegate.cc index 90ab5cbe1b2..6778545f415 100644 --- a/chromium/ui/views/controls/menu/menu_delegate.cc +++ b/chromium/ui/views/controls/menu/menu_delegate.cc @@ -2,9 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/views/controls/menu/menu_config.h" #include "ui/views/controls/menu/menu_delegate.h" +#include "ui/events/event.h" +#include "ui/views/controls/menu/menu_config.h" + namespace views { MenuDelegate::~MenuDelegate() {} @@ -13,14 +15,19 @@ bool MenuDelegate::IsItemChecked(int id) const { return false; } -string16 MenuDelegate::GetLabel(int id) const { - return string16(); +base::string16 MenuDelegate::GetLabel(int id) const { + return base::string16(); } -const gfx::Font* MenuDelegate::GetLabelFont(int id) const { +const gfx::FontList* MenuDelegate::GetLabelFontList(int id) const { return NULL; } +bool MenuDelegate::GetShouldUseDisabledEmphasizedForegroundColor( + int command_id) const { + return false; +} + bool MenuDelegate::GetBackgroundColor(int command_id, bool is_hovered, SkColor* override_color) const { @@ -33,12 +40,12 @@ bool MenuDelegate::GetForegroundColor(int command_id, return false; } -string16 MenuDelegate::GetTooltipText(int id, +base::string16 MenuDelegate::GetTooltipText(int id, const gfx::Point& screen_loc) const { - return string16(); + return base::string16(); } -bool MenuDelegate::GetAccelerator(int id, ui::Accelerator* accelerator) { +bool MenuDelegate::GetAccelerator(int id, ui::Accelerator* accelerator) const { return false; } @@ -57,7 +64,11 @@ bool MenuDelegate::IsCommandEnabled(int id) const { return true; } -bool MenuDelegate::GetContextualLabel(int id, string16* out) const { +bool MenuDelegate::IsCommandVisible(int id) const { + return true; +} + +bool MenuDelegate::GetContextualLabel(int id, base::string16* out) const { return false; } @@ -126,7 +137,7 @@ int MenuDelegate::GetDragOperations(MenuItemView* sender) { MenuItemView* MenuDelegate::GetSiblingMenu(MenuItemView* menu, const gfx::Point& screen_point, - MenuItemView::AnchorPosition* anchor, + MenuAnchorPosition* anchor, bool* has_mnemonics, MenuButton** button) { return NULL; diff --git a/chromium/ui/views/controls/menu/menu_delegate.h b/chromium/ui/views/controls/menu/menu_delegate.h index f171d570b7e..fafccd790f9 100644 --- a/chromium/ui/views/controls/menu/menu_delegate.h +++ b/chromium/ui/views/controls/menu/menu_delegate.h @@ -10,23 +10,29 @@ #include "base/logging.h" #include "base/strings/string16.h" +#include "third_party/skia/include/core/SkColor.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/os_exchange_data.h" -#include "ui/views/controls/menu/menu_item_view.h" +#include "ui/base/ui_base_types.h" +#include "ui/views/controls/menu/menu_types.h" +#include "ui/views/views_export.h" using ui::OSExchangeData; namespace gfx { -class Font; +class FontList; +class Point; } namespace ui { class Accelerator; +class DropTargetEvent; } namespace views { class MenuButton; +class MenuItemView; // MenuDelegate -------------------------------------------------------------- @@ -61,14 +67,21 @@ class VIEWS_EXPORT MenuDelegate { // The string shown for the menu item. This is only invoked when an item is // added with an empty label. - virtual string16 GetLabel(int id) const; + virtual base::string16 GetLabel(int id) const; // The font for the menu item label. - virtual const gfx::Font* GetLabelFont(int id) const; + virtual const gfx::FontList* GetLabelFontList(int id) const; + + // Whether this item should be displayed with a bolder color when disabled. + virtual bool GetShouldUseDisabledEmphasizedForegroundColor( + int command_id) const; // Override the text color of a given menu item dependent on the // |command_id| and its |is_hovered| state. Returns true if it chooses to // override the color. + // + // TODO(erg): Remove this interface. Injecting raw colors into the menu + // circumvents the NativeTheme. virtual bool GetForegroundColor(int command_id, bool is_hovered, SkColor* override_color) const; @@ -76,17 +89,21 @@ class VIEWS_EXPORT MenuDelegate { // Override the background color of a given menu item dependent on the // |command_id| and its |is_hovered| state. Returns true if it chooses to // override the color. + // + // TODO(erg): Remove this interface. Injecting raw colors into the menu + // circumvents the NativeTheme. virtual bool GetBackgroundColor(int command_id, bool is_hovered, SkColor* override_color) const; // The tooltip shown for the menu item. This is invoked when the user // hovers over the item, and no tooltip text has been set for that item. - virtual string16 GetTooltipText(int id, const gfx::Point& screen_loc) const; + virtual base::string16 GetTooltipText(int id, + const gfx::Point& screen_loc) const; // If there is an accelerator for the menu item with id |id| it is set in // |accelerator| and true is returned. - virtual bool GetAccelerator(int id, ui::Accelerator* accelerator); + virtual bool GetAccelerator(int id, ui::Accelerator* accelerator) const; // Shows the context menu with the specified id. This is invoked when the // user does the appropriate gesture to show a context menu. The id @@ -104,7 +121,8 @@ class VIEWS_EXPORT MenuDelegate { // Controller virtual bool SupportsCommand(int id) const; virtual bool IsCommandEnabled(int id) const; - virtual bool GetContextualLabel(int id, string16* out) const; + virtual bool IsCommandVisible(int id) const; + virtual bool GetContextualLabel(int id, base::string16* out) const; virtual void ExecuteCommand(int id) { } @@ -202,7 +220,7 @@ class VIEWS_EXPORT MenuDelegate { // The delegate owns the returned menu, not the controller. virtual MenuItemView* GetSiblingMenu(MenuItemView* menu, const gfx::Point& screen_point, - MenuItemView::AnchorPosition* anchor, + MenuAnchorPosition* anchor, bool* has_mnemonics, MenuButton** button); diff --git a/chromium/ui/views/controls/menu/menu_event_dispatcher_linux.cc b/chromium/ui/views/controls/menu/menu_event_dispatcher_linux.cc new file mode 100644 index 00000000000..181ba241afe --- /dev/null +++ b/chromium/ui/views/controls/menu/menu_event_dispatcher_linux.cc @@ -0,0 +1,91 @@ +// Copyright 2014 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/controls/menu/menu_event_dispatcher_linux.h" + +#include "base/memory/scoped_ptr.h" +#include "ui/aura/window.h" +#include "ui/events/event_utils.h" +#include "ui/events/keycodes/keyboard_code_conversion.h" +#include "ui/events/keycodes/keyboard_codes.h" +#include "ui/views/controls/menu/menu_controller.h" +#include "ui/views/widget/widget.h" + +namespace views { +namespace internal { + +MenuEventDispatcher::MenuEventDispatcher(MenuController* controller) + : menu_controller_(controller) {} + +MenuEventDispatcher::~MenuEventDispatcher() {} + +bool MenuEventDispatcher::CanDispatchEvent(const ui::PlatformEvent& event) { + return true; +} + +uint32_t MenuEventDispatcher::DispatchEvent(const ui::PlatformEvent& event) { + bool should_quit = false; + bool should_perform_default = true; + bool should_process_event = true; + + // Check if the event should be handled. + scoped_ptr<ui::Event> ui_event(ui::EventFromNative(event)); + if (ui_event && menu_controller_->owner()) { + aura::Window* menu_window = menu_controller_->owner()->GetNativeWindow(); + aura::Window* target_window = static_cast<aura::Window*>( + static_cast<ui::EventTarget*>(menu_window->GetRootWindow())-> + GetEventTargeter()->FindTargetForEvent(menu_window, + ui_event.get())); + // TODO(flackr): The event shouldn't be handled if target_window is not + // menu_window, however the event targeter does not properly target the + // open menu. For now, we allow targeters to prevent handling by the menu. + if (!target_window) + should_process_event = false; + } + + if (menu_controller_->exit_type() == MenuController::EXIT_ALL || + menu_controller_->exit_type() == MenuController::EXIT_DESTROYED) { + should_quit = true; + } else if (should_process_event) { + switch (ui::EventTypeFromNative(event)) { + case ui::ET_KEY_PRESSED: { + if (!menu_controller_->OnKeyDown(ui::KeyboardCodeFromNative(event))) { + should_quit = true; + should_perform_default = false; + break; + } + + // Do not check mnemonics if the Alt or Ctrl modifiers are pressed. + int flags = ui::EventFlagsFromNative(event); + if ((flags & (ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)) == 0) { + char c = ui::GetCharacterFromKeyCode( + ui::KeyboardCodeFromNative(event), flags); + if (menu_controller_->SelectByChar(c)) { + should_quit = true; + should_perform_default = false; + break; + } + } + should_quit = false; + should_perform_default = false; + break; + } + case ui::ET_KEY_RELEASED: + should_quit = false; + should_perform_default = false; + break; + default: + break; + } + } + + if (should_quit || menu_controller_->exit_type() != MenuController::EXIT_NONE) + menu_controller_->TerminateNestedMessageLoop(); + + return should_perform_default ? ui::POST_DISPATCH_PERFORM_DEFAULT + : ui::POST_DISPATCH_NONE; +} + +} // namespace internal +} // namespace views diff --git a/chromium/ui/views/controls/menu/menu_event_dispatcher_linux.h b/chromium/ui/views/controls/menu/menu_event_dispatcher_linux.h new file mode 100644 index 00000000000..bdc76fd88ba --- /dev/null +++ b/chromium/ui/views/controls/menu/menu_event_dispatcher_linux.h @@ -0,0 +1,32 @@ +// Copyright 2014 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/macros.h" +#include "ui/events/platform/platform_event_dispatcher.h" + +namespace views { + +class MenuController; + +namespace internal { + +// A message-pump dispatcher object used to dispatch events from the nested +// message-loop initiated by the MenuController. +class MenuEventDispatcher : public ui::PlatformEventDispatcher { + public: + explicit MenuEventDispatcher(MenuController* menu_controller); + virtual ~MenuEventDispatcher(); + + private: + // ui::PlatformEventDispatcher: + virtual bool CanDispatchEvent(const ui::PlatformEvent& event) OVERRIDE; + virtual uint32_t DispatchEvent(const ui::PlatformEvent& event) OVERRIDE; + + MenuController* menu_controller_; + + DISALLOW_COPY_AND_ASSIGN(MenuEventDispatcher); +}; + +} // namespace internal +} // namespace views diff --git a/chromium/ui/views/controls/menu/menu_host.cc b/chromium/ui/views/controls/menu/menu_host.cc index 4645e1ba6aa..2716ec8bb78 100644 --- a/chromium/ui/views/controls/menu/menu_host.cc +++ b/chromium/ui/views/controls/menu/menu_host.cc @@ -18,10 +18,6 @@ #include "ui/views/widget/native_widget_private.h" #include "ui/views/widget/widget.h" -#if defined(USE_AURA) -#include "ui/views/corewm/shadow_types.h" -#endif - namespace views { //////////////////////////////////////////////////////////////////////////////// @@ -49,7 +45,8 @@ void MenuHost::InitMenuHost(Widget* parent, bool rounded_border = menu_controller && menu_config.corner_radius > 0; bool bubble_border = submenu_->GetScrollViewContainer() && submenu_->GetScrollViewContainer()->HasBubbleBorder(); - params.has_dropshadow = !bubble_border; + params.shadow_type = bubble_border ? Widget::InitParams::SHADOW_TYPE_NONE + : Widget::InitParams::SHADOW_TYPE_DROP; params.opacity = (bubble_border || rounded_border) ? Widget::InitParams::TRANSLUCENT_WINDOW : Widget::InitParams::OPAQUE_WINDOW; @@ -57,14 +54,7 @@ void MenuHost::InitMenuHost(Widget* parent, params.bounds = bounds; Init(params); -#if defined(USE_AURA) - if (bubble_border) - SetShadowType(GetNativeView(), views::corewm::SHADOW_TYPE_NONE); -#endif - SetContentsView(contents_view); - if (bubble_border || rounded_border) - SetOpacity(0); ShowMenuHost(do_capture); } @@ -76,13 +66,11 @@ void MenuHost::ShowMenuHost(bool do_capture) { // Doing a capture may make us get capture lost. Ignore it while we're in the // process of showing. base::AutoReset<bool> reseter(&ignore_capture_lost_, true); - Show(); + ShowInactive(); if (do_capture) { -#if defined(USE_AURA) // Cancel existing touches, so we don't miss some touch release/cancel // events due to the menu taking capture. - ui::GestureRecognizer::Get()->TransferEventsTo(GetNativeWindow(), NULL); -#endif // USE_AURA + ui::GestureRecognizer::Get()->TransferEventsTo(NULL, NULL); native_widget_private()->SetCapture(); } } diff --git a/chromium/ui/views/controls/menu/menu_host_root_view.cc b/chromium/ui/views/controls/menu/menu_host_root_view.cc index 2577d909b7b..3bce4a49b6e 100644 --- a/chromium/ui/views/controls/menu/menu_host_root_view.cc +++ b/chromium/ui/views/controls/menu/menu_host_root_view.cc @@ -51,17 +51,8 @@ void MenuHostRootView::OnMouseMoved(const ui::MouseEvent& event) { } bool MenuHostRootView::OnMouseWheel(const ui::MouseWheelEvent& event) { -#if defined(USE_AURA) - // Aura uses MenuController to forward events like other mouse events. return GetMenuController() && GetMenuController()->OnMouseWheel(submenu_, event); -#else - // Windows uses focus_util_win::RerouteMouseWheel to forward events to - // the right menu. - // RootView::OnMouseWheel forwards to the focused view. We don't have a - // focused view, so we need to override this then forward to the menu. - return submenu_->OnMouseWheel(event); -#endif } void MenuHostRootView::DispatchGestureEvent(ui::GestureEvent* event) { diff --git a/chromium/ui/views/controls/menu/menu_image_util.cc b/chromium/ui/views/controls/menu/menu_image_util.cc index a50980b3500..60315736fe2 100644 --- a/chromium/ui/views/controls/menu/menu_image_util.cc +++ b/chromium/ui/views/controls/menu/menu_image_util.cc @@ -54,7 +54,7 @@ class RadioButtonImageSource : public gfx::CanvasImageSource { skia::RefPtr<SkShader> shader = skia::AdoptRef( SkGradientShader::CreateLinear( gradient_points, gradient_colors, NULL, arraysize(gradient_points), - SkShader::kClamp_TileMode, NULL)); + SkShader::kClamp_TileMode)); SkPaint paint; paint.setStyle(SkPaint::kFill_Style); paint.setAntiAlias(true); @@ -76,8 +76,7 @@ class RadioButtonImageSource : public gfx::CanvasImageSource { shader = skia::AdoptRef( SkGradientShader::CreateLinear( selected_gradient_points, selected_gradient_colors, NULL, - arraysize(selected_gradient_points), - SkShader::kClamp_TileMode, NULL)); + arraysize(selected_gradient_points), SkShader::kClamp_TileMode)); paint.setShader(shader.get()); paint.setStyle(SkPaint::kFill_Style); canvas->sk_canvas()->drawCircle(radius, radius, diff --git a/chromium/ui/views/controls/menu/menu_item_view.cc b/chromium/ui/views/controls/menu/menu_item_view.cc index 2adc1ad2f17..f24c61059c6 100644 --- a/chromium/ui/views/controls/menu/menu_item_view.cc +++ b/chromium/ui/views/controls/menu/menu_item_view.cc @@ -9,12 +9,15 @@ #include "base/strings/utf_string_conversions.h" #include "grit/ui_resources.h" #include "grit/ui_strings.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/models/menu_model.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/vector2d.h" #include "ui/gfx/image/image.h" +#include "ui/gfx/text_utils.h" #include "ui/native_theme/common_theme.h" #include "ui/views/controls/button/menu_button.h" #include "ui/views/controls/image_view.h" @@ -47,7 +50,7 @@ class EmptyMenuMenuItem : public MenuItemView { } virtual bool GetTooltipText(const gfx::Point& p, - string16* tooltip) const OVERRIDE { + base::string16* tooltip) const OVERRIDE { // Empty menu items shouldn't have a tooltip. return false; } @@ -116,7 +119,7 @@ void MenuItemView::ChildPreferredSizeChanged(View* child) { } bool MenuItemView::GetTooltipText(const gfx::Point& p, - string16* tooltip) const { + base::string16* tooltip) const { *tooltip = tooltip_; if (!tooltip->empty()) return true; @@ -147,15 +150,15 @@ bool MenuItemView::GetTooltipText(const gfx::Point& p, return !tooltip->empty(); } -void MenuItemView::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_MENUITEM; +void MenuItemView::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_MENU_ITEM; - string16 item_text; + base::string16 item_text; if (IsContainer()) { // The first child is taking over, just use its accessible name instead of // |title_|. View* child = child_at(0); - ui::AccessibleViewState state; + ui::AXViewState state; child->GetAccessibleState(&state); item_text = state.name; } else { @@ -165,12 +168,12 @@ void MenuItemView::GetAccessibleState(ui::AccessibleViewState* state) { switch (GetType()) { case SUBMENU: - state->state |= ui::AccessibilityTypes::STATE_HASPOPUP; + state->AddStateFlag(ui::AX_STATE_HASPOPUP); break; case CHECKBOX: case RADIO: - state->state |= GetDelegate()->IsItemChecked(GetCommand()) ? - ui::AccessibilityTypes::STATE_CHECKED : 0; + if (GetDelegate()->IsItemChecked(GetCommand())) + state->AddStateFlag(ui::AX_STATE_CHECKED); break; case NORMAL: case SEPARATOR: @@ -181,22 +184,22 @@ void MenuItemView::GetAccessibleState(ui::AccessibleViewState* state) { } // static -bool MenuItemView::IsBubble(MenuItemView::AnchorPosition anchor) { - return anchor == MenuItemView::BUBBLE_LEFT || - anchor == MenuItemView::BUBBLE_RIGHT || - anchor == MenuItemView::BUBBLE_ABOVE || - anchor == MenuItemView::BUBBLE_BELOW; +bool MenuItemView::IsBubble(MenuAnchorPosition anchor) { + return anchor == MENU_ANCHOR_BUBBLE_LEFT || + anchor == MENU_ANCHOR_BUBBLE_RIGHT || + anchor == MENU_ANCHOR_BUBBLE_ABOVE || + anchor == MENU_ANCHOR_BUBBLE_BELOW; } // static -string16 MenuItemView::GetAccessibleNameForMenuItem( - const string16& item_text, const string16& minor_text) { - string16 accessible_name = item_text; +base::string16 MenuItemView::GetAccessibleNameForMenuItem( + const base::string16& item_text, const base::string16& minor_text) { + base::string16 accessible_name = item_text; // Filter out the "&" for accessibility clients. size_t index = 0; - const char16 amp = '&'; - while ((index = accessible_name.find(amp, index)) != string16::npos && + const base::char16 amp = '&'; + while ((index = accessible_name.find(amp, index)) != base::string16::npos && index + 1 < accessible_name.length()) { accessible_name.replace(index, accessible_name.length() - index, accessible_name.substr(index + 1)); @@ -225,9 +228,9 @@ void MenuItemView::Cancel() { MenuItemView* MenuItemView::AddMenuItemAt( int index, int item_id, - const string16& label, - const string16& sublabel, - const string16& minor_text, + const base::string16& label, + const base::string16& sublabel, + const base::string16& minor_text, const gfx::ImageSkia& icon, Type type, ui::MenuSeparatorType separator_style) { @@ -251,6 +254,8 @@ MenuItemView* MenuItemView::AddMenuItemAt( item->SetIcon(icon); if (type == SUBMENU) item->CreateSubmenu(); + if (GetDelegate() && !GetDelegate()->IsCommandVisible(item_id)) + item->SetVisible(false); submenu_->AddChildViewAt(item, index); return item; } @@ -271,51 +276,52 @@ void MenuItemView::RemoveMenuItemAt(int index) { } MenuItemView* MenuItemView::AppendMenuItem(int item_id, - const string16& label, + const base::string16& label, Type type) { - return AppendMenuItemImpl(item_id, label, string16(), string16(), + return AppendMenuItemImpl(item_id, label, base::string16(), base::string16(), gfx::ImageSkia(), type, ui::NORMAL_SEPARATOR); } MenuItemView* MenuItemView::AppendSubMenu(int item_id, - const string16& label) { - return AppendMenuItemImpl(item_id, label, string16(), string16(), + const base::string16& label) { + return AppendMenuItemImpl(item_id, label, base::string16(), base::string16(), gfx::ImageSkia(), SUBMENU, ui::NORMAL_SEPARATOR); } MenuItemView* MenuItemView::AppendSubMenuWithIcon(int item_id, - const string16& label, + const base::string16& label, const gfx::ImageSkia& icon) { - return AppendMenuItemImpl(item_id, label, string16(), string16(), icon, - SUBMENU, ui::NORMAL_SEPARATOR); + return AppendMenuItemImpl(item_id, label, base::string16(), base::string16(), + icon, SUBMENU, ui::NORMAL_SEPARATOR); } -MenuItemView* MenuItemView::AppendMenuItemWithLabel(int item_id, - const string16& label) { +MenuItemView* MenuItemView::AppendMenuItemWithLabel( + int item_id, + const base::string16& label) { return AppendMenuItem(item_id, label, NORMAL); } MenuItemView* MenuItemView::AppendDelegateMenuItem(int item_id) { - return AppendMenuItem(item_id, string16(), NORMAL); + return AppendMenuItem(item_id, base::string16(), NORMAL); } void MenuItemView::AppendSeparator() { - AppendMenuItemImpl(0, string16(), string16(), string16(), gfx::ImageSkia(), - SEPARATOR, ui::NORMAL_SEPARATOR); + AppendMenuItemImpl(0, base::string16(), base::string16(), base::string16(), + gfx::ImageSkia(), SEPARATOR, ui::NORMAL_SEPARATOR); } MenuItemView* MenuItemView::AppendMenuItemWithIcon(int item_id, - const string16& label, + const base::string16& label, const gfx::ImageSkia& icon) { - return AppendMenuItemImpl(item_id, label, string16(), string16(), icon, - NORMAL, ui::NORMAL_SEPARATOR); + return AppendMenuItemImpl(item_id, label, base::string16(), base::string16(), + icon, NORMAL, ui::NORMAL_SEPARATOR); } MenuItemView* MenuItemView::AppendMenuItemImpl( int item_id, - const string16& label, - const string16& sublabel, - const string16& minor_text, + const base::string16& label, + const base::string16& sublabel, + const base::string16& minor_text, const gfx::ImageSkia& icon, Type type, ui::MenuSeparatorType separator_style) { @@ -338,17 +344,17 @@ SubmenuView* MenuItemView::GetSubmenu() const { return submenu_; } -void MenuItemView::SetTitle(const string16& title) { +void MenuItemView::SetTitle(const base::string16& title) { title_ = title; invalidate_dimensions(); // Triggers preferred size recalculation. } -void MenuItemView::SetSubtitle(const string16& subtitle) { +void MenuItemView::SetSubtitle(const base::string16& subtitle) { subtitle_ = subtitle; invalidate_dimensions(); // Triggers preferred size recalculation. } -void MenuItemView::SetMinorText(const string16& minor_text) { +void MenuItemView::SetMinorText(const base::string16& minor_text) { minor_text_ = minor_text; invalidate_dimensions(); // Triggers preferred size recalculation. } @@ -358,7 +364,7 @@ void MenuItemView::SetSelected(bool selected) { SchedulePaint(); } -void MenuItemView::SetTooltip(const string16& tooltip, int item_id) { +void MenuItemView::SetTooltip(const base::string16& tooltip, int item_id) { MenuItemView* item = GetMenuItemByID(item_id); DCHECK(item); item->tooltip_ = tooltip; @@ -399,13 +405,13 @@ void MenuItemView::OnPaint(gfx::Canvas* canvas) { PaintButton(canvas, PB_NORMAL); } -gfx::Size MenuItemView::GetPreferredSize() { +gfx::Size MenuItemView::GetPreferredSize() const { const MenuItemDimensions& dimensions(GetDimensions()); return gfx::Size(dimensions.standard_width + dimensions.children_width, dimensions.height); } -const MenuItemView::MenuItemDimensions& MenuItemView::GetDimensions() { +const MenuItemView::MenuItemDimensions& MenuItemView::GetDimensions() const { if (!is_dimensions_valid()) dimensions_ = CalculateDimensions(); DCHECK(is_dimensions_valid()); @@ -441,16 +447,16 @@ const MenuItemView* MenuItemView::GetRootMenuItem() const { return item; } -char16 MenuItemView::GetMnemonic() { +base::char16 MenuItemView::GetMnemonic() { if (!GetRootMenuItem()->has_mnemonics_) return 0; size_t index = 0; do { index = title_.find('&', index); - if (index != string16::npos) { + if (index != base::string16::npos) { if (index + 1 != title_.size() && title_[index + 1] != '&') { - char16 char_array[] = { title_[index + 1], 0 }; + base::char16 char_array[] = { title_[index + 1], 0 }; // TODO(jshin): What about Turkish locale? See http://crbug.com/81719. // If the mnemonic is capital I and the UI language is Turkish, // lowercasing it results in 'small dotless i', which is different @@ -459,7 +465,7 @@ char16 MenuItemView::GetMnemonic() { } index++; } - } while (index != string16::npos); + } while (index != base::string16::npos); return 0; } @@ -685,14 +691,14 @@ int MenuItemView::GetDrawStringFlags() { return flags; } -const gfx::Font& MenuItemView::GetFont() { +const gfx::FontList& MenuItemView::GetFontList() const { const MenuDelegate* delegate = GetDelegate(); if (delegate) { - const gfx::Font* font = delegate->GetLabelFont(GetCommand()); - if (font) - return *font; + const gfx::FontList* font_list = delegate->GetLabelFontList(GetCommand()); + if (font_list) + return *font_list; } - return GetMenuConfig().font; + return GetMenuConfig().font_list; } void MenuItemView::AddEmptyMenus() { @@ -738,13 +744,6 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { parent_menu_item_->GetSubmenu()->GetShowSelection(this) && (NonIconChildViewsCount() == 0)); - int icon_x = config.item_left_margin + left_icon_margin_; - int top_margin = GetTopMargin(); - int bottom_margin = GetBottomMargin(); - int icon_y = top_margin + (height() - config.item_top_margin - - bottom_margin - config.check_height) / 2; - int icon_height = config.check_height; - int available_height = height() - top_margin - bottom_margin; MenuDelegate *delegate = GetDelegate(); // Render the background. As MenuScrollViewContainer draws the background, we // only need the background when we want it to look different, as when we're @@ -766,21 +765,27 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { ui::NativeTheme::ExtraParams()); } + const int icon_x = config.item_left_margin + left_icon_margin_; + const int top_margin = GetTopMargin(); + const int bottom_margin = GetBottomMargin(); + const int available_height = height() - top_margin - bottom_margin; + // Render the check. if (type_ == CHECKBOX && delegate->IsItemChecked(GetCommand())) { - gfx::ImageSkia check = GetMenuCheckImage(IsSelected()); + gfx::ImageSkia check = GetMenuCheckImage(render_selection); // Don't use config.check_width here as it's padded // to force more padding (AURA). - gfx::Rect check_bounds(icon_x, icon_y, check.width(), icon_height); + gfx::Rect check_bounds(icon_x, + top_margin + (available_height - check.height()) / 2, + check.width(), + check.height()); AdjustBoundsForRTLUI(&check_bounds); canvas->DrawImageInt(check, check_bounds.x(), check_bounds.y()); } else if (type_ == RADIO) { gfx::ImageSkia image = GetRadioButtonImage(delegate->IsItemChecked(GetCommand())); gfx::Rect radio_bounds(icon_x, - top_margin + - (height() - top_margin - bottom_margin - - image.height()) / 2, + top_margin + (available_height - image.height()) / 2, image.width(), image.height()); AdjustBoundsForRTLUI(&radio_bounds); @@ -788,12 +793,18 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { } // Render the foreground. - ui::NativeTheme::ColorId color_id = - ui::NativeTheme::kColorId_DisabledMenuItemForegroundColor; + ui::NativeTheme::ColorId color_id; if (enabled()) { color_id = render_selection ? ui::NativeTheme::kColorId_SelectedMenuItemForegroundColor: ui::NativeTheme::kColorId_EnabledMenuItemForegroundColor; + } else { + bool emphasized = delegate && + delegate->GetShouldUseDisabledEmphasizedForegroundColor( + GetCommand()); + color_id = emphasized ? + ui::NativeTheme::kColorId_DisabledEmphasizedMenuItemForegroundColor : + ui::NativeTheme::kColorId_DisabledMenuItemForegroundColor; } SkColor fg_color = native_theme->GetSystemColor(color_id); SkColor override_foreground_color; @@ -802,7 +813,7 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { &override_foreground_color)) fg_color = override_foreground_color; - const gfx::Font& font = GetFont(); + const gfx::FontList& font_list = GetFontList(); int accel_width = parent_menu_item_->GetSubmenu()->max_minor_text_width(); int label_start = GetLabelStartForThisItem(); @@ -817,19 +828,15 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { int flags = GetDrawStringFlags(); if (mode == PB_FOR_DRAG) flags |= gfx::Canvas::NO_SUBPIXEL_RENDERING; - canvas->DrawStringInt(title(), font, fg_color, - text_bounds.x(), text_bounds.y(), text_bounds.width(), - text_bounds.height(), flags); + canvas->DrawStringRectWithFlags(title(), font_list, fg_color, text_bounds, + flags); if (!subtitle_.empty()) { - canvas->DrawStringInt( + canvas->DrawStringRectWithFlags( subtitle_, - font, + font_list, GetNativeTheme()->GetSystemColor( ui::NativeTheme::kColorId_ButtonDisabledColor), - text_bounds.x(), - text_bounds.y() + GetFont().GetHeight(), - text_bounds.width(), - text_bounds.height(), + text_bounds + gfx::Vector2d(0, font_list.GetHeight()), flags); } @@ -837,24 +844,23 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { // Render the submenu indicator (arrow). if (HasSubmenu()) { + gfx::ImageSkia arrow = GetSubmenuArrowImage(render_selection); gfx::Rect arrow_bounds(this->width() - config.arrow_width - config.arrow_to_edge_padding, - top_margin + (available_height - - config.arrow_width) / 2, - config.arrow_width, height()); + top_margin + (available_height - arrow.height()) / 2, + config.arrow_width, + arrow.height()); AdjustBoundsForRTLUI(&arrow_bounds); - canvas->DrawImageInt(GetSubmenuArrowImage(IsSelected()), - arrow_bounds.x(), arrow_bounds.y()); + canvas->DrawImageInt(arrow, arrow_bounds.x(), arrow_bounds.y()); } } void MenuItemView::PaintMinorText(gfx::Canvas* canvas, bool render_selection) { - string16 minor_text = GetMinorText(); + base::string16 minor_text = GetMinorText(); if (minor_text.empty()) return; - const gfx::Font& font = GetFont(); int available_height = height() - GetTopMargin() - GetBottomMargin(); int max_accel_width = parent_menu_item_->GetSubmenu()->max_minor_text_width(); @@ -870,16 +876,13 @@ void MenuItemView::PaintMinorText(gfx::Canvas* canvas, flags |= gfx::Canvas::TEXT_ALIGN_LEFT; else flags |= gfx::Canvas::TEXT_ALIGN_RIGHT; - canvas->DrawStringInt( + canvas->DrawStringRectWithFlags( minor_text, - font, + GetFontList(), GetNativeTheme()->GetSystemColor(render_selection ? ui::NativeTheme::kColorId_SelectedMenuItemForegroundColor : ui::NativeTheme::kColorId_ButtonDisabledColor), - accel_bounds.x(), - accel_bounds.y(), - accel_bounds.width(), - accel_bounds.height(), + accel_bounds, flags); } @@ -894,38 +897,36 @@ void MenuItemView::DestroyAllMenuHosts() { } } -int MenuItemView::GetTopMargin() { +int MenuItemView::GetTopMargin() const { if (top_margin_ >= 0) return top_margin_; - MenuItemView* root = GetRootMenuItem(); + const MenuItemView* root = GetRootMenuItem(); return root && root->has_icons_ ? GetMenuConfig().item_top_margin : GetMenuConfig().item_no_icon_top_margin; } -int MenuItemView::GetBottomMargin() { +int MenuItemView::GetBottomMargin() const { if (bottom_margin_ >= 0) return bottom_margin_; - MenuItemView* root = GetRootMenuItem(); + const MenuItemView* root = GetRootMenuItem(); return root && root->has_icons_ ? GetMenuConfig().item_bottom_margin : GetMenuConfig().item_no_icon_bottom_margin; } -gfx::Size MenuItemView::GetChildPreferredSize() { +gfx::Size MenuItemView::GetChildPreferredSize() const { if (!has_children()) return gfx::Size(); - if (IsContainer()) { - View* child = child_at(0); - return child->GetPreferredSize(); - } + if (IsContainer()) + return child_at(0)->GetPreferredSize(); int width = 0; for (int i = 0; i < child_count(); ++i) { - View* child = child_at(i); + const View* child = child_at(i); if (icon_view_ && (icon_view_ == child)) continue; if (i) @@ -941,7 +942,7 @@ gfx::Size MenuItemView::GetChildPreferredSize() { return gfx::Size(width, height); } -MenuItemView::MenuItemDimensions MenuItemView::CalculateDimensions() { +MenuItemView::MenuItemDimensions MenuItemView::CalculateDimensions() const { gfx::Size child_size = GetChildPreferredSize(); MenuItemDimensions dimensions; @@ -961,7 +962,7 @@ MenuItemView::MenuItemDimensions MenuItemView::CalculateDimensions() { return dimensions; // Determine the length of the label text. - const gfx::Font& font = GetFont(); + const gfx::FontList& font_list = GetFontList(); // Get Icon margin overrides for this particular item. const MenuDelegate* delegate = GetDelegate(); @@ -976,27 +977,30 @@ MenuItemView::MenuItemDimensions MenuItemView::CalculateDimensions() { } int label_start = GetLabelStartForThisItem(); - int string_width = font.GetStringWidth(title_); - if (!subtitle_.empty()) - string_width = std::max(string_width, font.GetStringWidth(subtitle_)); + int string_width = gfx::GetStringWidth(title_, font_list); + if (!subtitle_.empty()) { + string_width = std::max(string_width, + gfx::GetStringWidth(subtitle_, font_list)); + } dimensions.standard_width = string_width + label_start + item_right_margin_; // Determine the length of the right-side text. - string16 minor_text = GetMinorText(); + base::string16 minor_text = GetMinorText(); dimensions.minor_text_width = - minor_text.empty() ? 0 : GetFont().GetStringWidth(minor_text); + minor_text.empty() ? 0 : gfx::GetStringWidth(minor_text, font_list); // Determine the height to use. + dimensions.height = + std::max(dimensions.height, + (subtitle_.empty() ? 0 : font_list.GetHeight()) + + font_list.GetHeight() + GetBottomMargin() + GetTopMargin()); dimensions.height = std::max(dimensions.height, - (subtitle_.empty() ? 0 : font.GetHeight()) + - font.GetHeight() + GetBottomMargin() + GetTopMargin()); - dimensions.height = std::max(dimensions.height, - GetMenuConfig().item_min_height); + GetMenuConfig().item_min_height); return dimensions; } -int MenuItemView::GetLabelStartForThisItem() { +int MenuItemView::GetLabelStartForThisItem() const { int label_start = label_start_ + left_icon_margin_ + right_icon_margin_; if ((type_ == CHECKBOX || type_ == RADIO) && icon_view_) { label_start += icon_view_->size().width() + @@ -1005,10 +1009,10 @@ int MenuItemView::GetLabelStartForThisItem() { return label_start; } -string16 MenuItemView::GetMinorText() { +base::string16 MenuItemView::GetMinorText() const { if (id() == kEmptyMenuItemViewID) { // Don't query the delegate for menus that represent no children. - return string16(); + return base::string16(); } ui::Accelerator accelerator; diff --git a/chromium/ui/views/controls/menu/menu_item_view.h b/chromium/ui/views/controls/menu/menu_item_view.h index 2d394bae2f2..4f0ea441caa 100644 --- a/chromium/ui/views/controls/menu/menu_item_view.h +++ b/chromium/ui/views/controls/menu/menu_item_view.h @@ -15,6 +15,7 @@ #include "ui/base/models/menu_separator_types.h" #include "ui/gfx/image/image_skia.h" #include "ui/views/controls/menu/menu_config.h" +#include "ui/views/controls/menu/menu_types.h" #include "ui/views/view.h" #if defined(OS_WIN) @@ -24,7 +25,7 @@ #endif namespace gfx { -class Font; +class FontList; } namespace views { @@ -83,21 +84,6 @@ class VIEWS_EXPORT MenuItemView : public View { EMPTY }; - // Where the menu should be anchored to for non-RTL languages. The - // opposite position will be used if base::i18n:IsRTL() is true. - // The BUBBLE flags are used when the menu should get enclosed by a bubble. - // Note that BUBBLE flags should only be used with menus which have no - // children. - enum AnchorPosition { - TOPLEFT, - TOPRIGHT, - BOTTOMCENTER, - BUBBLE_LEFT, - BUBBLE_RIGHT, - BUBBLE_ABOVE, - BUBBLE_BELOW - }; - // Where the menu should be drawn, above or below the bounds (when // the bounds is non-empty). POSITION_BEST_FIT (default) positions // the menu below the bounds unless the menu does not fit on the @@ -132,8 +118,8 @@ class VIEWS_EXPORT MenuItemView : public View { // Overridden from View: virtual bool GetTooltipText(const gfx::Point& p, - string16* tooltip) const OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + base::string16* tooltip) const OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; // Returns the preferred height of menu items. This is only valid when the // menu is about to be shown. @@ -143,12 +129,12 @@ class VIEWS_EXPORT MenuItemView : public View { static int label_start() { return label_start_; } // Returns if a given |anchor| is a bubble or not. - static bool IsBubble(MenuItemView::AnchorPosition anchor); + static bool IsBubble(MenuAnchorPosition anchor); // Returns the accessible name to be used with screen readers. Mnemonics are // removed and the menu item accelerator text is appended. - static string16 GetAccessibleNameForMenuItem( - const string16& item_text, const string16& accelerator_text); + static base::string16 GetAccessibleNameForMenuItem( + const base::string16& item_text, const base::string16& accelerator_text); // Hides and cancels the menu. This does nothing if the menu is not open. void Cancel(); @@ -157,9 +143,9 @@ class VIEWS_EXPORT MenuItemView : public View { // called after adding menu items if the menu may be active. MenuItemView* AddMenuItemAt(int index, int item_id, - const string16& label, - const string16& sublabel, - const string16& minor_text, + const base::string16& label, + const base::string16& sublabel, + const base::string16& minor_text, const gfx::ImageSkia& icon, Type type, ui::MenuSeparatorType separator_style); @@ -177,24 +163,24 @@ class VIEWS_EXPORT MenuItemView : public View { // label The text label shown. // type The type of item. MenuItemView* AppendMenuItem(int item_id, - const string16& label, + const base::string16& label, Type type); // Append a submenu to this menu. // The returned pointer is owned by this menu. MenuItemView* AppendSubMenu(int item_id, - const string16& label); + const base::string16& label); // Append a submenu with an icon to this menu. // The returned pointer is owned by this menu. MenuItemView* AppendSubMenuWithIcon(int item_id, - const string16& label, + const base::string16& label, const gfx::ImageSkia& icon); // This is a convenience for standard text label menu items where the label // is provided with this call. MenuItemView* AppendMenuItemWithLabel(int item_id, - const string16& label); + const base::string16& label); // This is a convenience for text label menu items where the label is // provided by the delegate. @@ -207,14 +193,14 @@ class VIEWS_EXPORT MenuItemView : public View { // needs an icon. Calling this function forces the Menu class to draw // the menu, instead of relying on Windows. MenuItemView* AppendMenuItemWithIcon(int item_id, - const string16& label, + const base::string16& label, const gfx::ImageSkia& icon); // All the AppendXXX methods funnel into this. MenuItemView* AppendMenuItemImpl(int item_id, - const string16& label, - const string16& sublabel, - const string16& minor_text, + const base::string16& label, + const base::string16& sublabel, + const base::string16& minor_text, const gfx::ImageSkia& icon, Type type, ui::MenuSeparatorType separator_style); @@ -234,14 +220,14 @@ class VIEWS_EXPORT MenuItemView : public View { const MenuItemView* GetParentMenuItem() const { return parent_menu_item_; } // Sets/Gets the title. - void SetTitle(const string16& title); - const string16& title() const { return title_; } + void SetTitle(const base::string16& title); + const base::string16& title() const { return title_; } // Sets the subtitle. - void SetSubtitle(const string16& subtitle); + void SetSubtitle(const base::string16& subtitle); // Sets the minor text. - void SetMinorText(const string16& minor_text); + void SetMinorText(const base::string16& minor_text); // Returns the type of this menu. const Type& GetType() const { return type_; } @@ -254,7 +240,7 @@ class VIEWS_EXPORT MenuItemView : public View { bool IsSelected() const { return selected_; } // Sets the |tooltip| for a menu item view with |item_id| identifier. - void SetTooltip(const string16& tooltip, int item_id); + void SetTooltip(const base::string16& tooltip, int item_id); // Sets the icon for the descendant identified by item_id. void SetIcon(const gfx::ImageSkia& icon, int item_id); @@ -277,10 +263,10 @@ class VIEWS_EXPORT MenuItemView : public View { virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; // Returns the preferred size of this item. - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; // Return the preferred dimensions of the item in pixel. - const MenuItemDimensions& GetDimensions(); + const MenuItemDimensions& GetDimensions() const; // Returns the object responsible for controlling showing the menu. MenuController* GetMenuController(); @@ -297,7 +283,7 @@ class VIEWS_EXPORT MenuItemView : public View { // Returns the mnemonic for this MenuItemView, or 0 if this MenuItemView // doesn't have a mnemonic. - char16 GetMnemonic(); + base::char16 GetMnemonic(); // Do we have icons? This only has effect on the top menu. Turning this on // makes the menus slightly wider and taller. @@ -344,11 +330,11 @@ class VIEWS_EXPORT MenuItemView : public View { virtual const char* GetClassName() const OVERRIDE; // Returns the preferred size (and padding) of any children. - virtual gfx::Size GetChildPreferredSize(); + virtual gfx::Size GetChildPreferredSize() const; // Returns the various margins. - int GetTopMargin(); - int GetBottomMargin(); + int GetTopMargin() const; + int GetBottomMargin() const; private: friend class internal::MenuRunnerImpl; // For access to ~MenuItemView. @@ -372,11 +358,11 @@ class VIEWS_EXPORT MenuItemView : public View { bool has_mnemonics, bool show_mnemonics); - // Returns the flags passed to DrawStringInt. + // Returns the flags passed to DrawStringRect. int GetDrawStringFlags(); - // Returns the font to use for menu text. - const gfx::Font& GetFont(); + // Returns the font list to use for menu text. + const gfx::FontList& GetFontList() const; // If this menu item has no children a child is added showing it has no // children. Otherwise AddEmtpyMenus is recursively invoked on child menu @@ -403,13 +389,13 @@ class VIEWS_EXPORT MenuItemView : public View { // Returns the text that should be displayed on the end (right) of the menu // item. This will be the accelerator (if one exists), otherwise |subtitle_|. - string16 GetMinorText(); + base::string16 GetMinorText() const; // Calculates and returns the MenuItemDimensions. - MenuItemDimensions CalculateDimensions(); + MenuItemDimensions CalculateDimensions() const; // Get the horizontal position at which to draw the menu item's label. - int GetLabelStartForThisItem(); + int GetLabelStartForThisItem() const; // Used by MenuController to cache the menu position in use by the // active menu. @@ -464,13 +450,13 @@ class VIEWS_EXPORT MenuItemView : public View { SubmenuView* submenu_; // Title. - string16 title_; + base::string16 title_; // Subtitle/sublabel. - string16 subtitle_; + base::string16 subtitle_; // Minor text. - string16 minor_text_; + base::string16 minor_text_; // Does the title have a mnemonic? Only useful on the root menu item. bool has_mnemonics_; @@ -486,7 +472,7 @@ class VIEWS_EXPORT MenuItemView : public View { View* icon_view_; // The tooltip to show on hover for this menu item. - string16 tooltip_; + base::string16 tooltip_; // Width of a menu icon area. static int icon_area_width_; @@ -502,7 +488,7 @@ class VIEWS_EXPORT MenuItemView : public View { // Cached dimensions. This is cached as text sizing calculations are quite // costly. - MenuItemDimensions dimensions_; + mutable MenuItemDimensions dimensions_; // Removed items to be deleted in ChildrenChanged(). std::vector<View*> removed_items_; @@ -513,8 +499,8 @@ class VIEWS_EXPORT MenuItemView : public View { // Horizontal icon margins in pixels, which can differ between MenuItems. // These values will be set in the layout process. - int left_icon_margin_; - int right_icon_margin_; + mutable int left_icon_margin_; + mutable int right_icon_margin_; // |menu_position_| is the requested position with respect to the bounds. // |actual_menu_position_| is used by the controller to cache the diff --git a/chromium/ui/views/controls/menu/menu_message_loop.h b/chromium/ui/views/controls/menu/menu_message_loop.h new file mode 100644 index 00000000000..a435d85f402 --- /dev/null +++ b/chromium/ui/views/controls/menu/menu_message_loop.h @@ -0,0 +1,57 @@ +// Copyright 2014 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_CONTROLS_MENU_MENU_MESSAGE_LOOP_H_ +#define UI_VIEWS_CONTROLS_MENU_MENU_MESSAGE_LOOP_H_ + +#include "ui/gfx/native_widget_types.h" + +namespace gfx { +class Point; +} + +namespace ui { +class LocatedEvent; +} + +namespace views { + +class MenuController; +class Widget; + +// Interface used by MenuController to run a nested message loop while +// showing a menu, allowing for platform specific implementations. +class MenuMessageLoop { + public: + virtual ~MenuMessageLoop() {} + + // Create a platform specific instance. + static MenuMessageLoop* Create(); + + // Runs the platform specific bits of the message loop. If |nested_menu| is + // true we're being asked to run a menu from within a menu (eg a context + // menu). + virtual void Run(MenuController*, Widget* owner, bool nested_menu) = 0; + + // Returns true if it is a good time to call QuitNow(). + // Returns false otherwise, for example if a drag and drop is in progress. + virtual bool ShouldQuitNow() const = 0; + + // Quit an earlier call to Run(). + virtual void QuitNow() = 0; + + // Repost |event| to |window|. + // |screen_loc| is the event's location in screen coordinates. + virtual void RepostEventToWindow(const ui::LocatedEvent& event, + gfx::NativeWindow window, + const gfx::Point& screen_loc) = 0; + + // Clear any references to the owner widget that was passed into the previous + // call to Run(). + virtual void ClearOwner() = 0; +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_MENU_MENU_MESSAGE_LOOP_H_ diff --git a/chromium/ui/views/controls/menu/menu_message_loop_aura.cc b/chromium/ui/views/controls/menu/menu_message_loop_aura.cc new file mode 100644 index 00000000000..0d7e99aab03 --- /dev/null +++ b/chromium/ui/views/controls/menu/menu_message_loop_aura.cc @@ -0,0 +1,202 @@ +// Copyright 2014 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/controls/menu/menu_message_loop_aura.h" + +#if defined(OS_WIN) +#include <windowsx.h> +#endif + +#include "base/run_loop.h" +#include "ui/aura/client/screen_position_client.h" +#include "ui/aura/env.h" +#include "ui/aura/window.h" +#include "ui/aura/window_event_dispatcher.h" +#include "ui/aura/window_tree_host.h" +#include "ui/events/event.h" +#include "ui/events/platform/platform_event_source.h" +#include "ui/events/platform/scoped_event_dispatcher.h" +#include "ui/views/controls/menu/menu_controller.h" +#include "ui/views/widget/widget.h" +#include "ui/wm/public/activation_change_observer.h" +#include "ui/wm/public/activation_client.h" +#include "ui/wm/public/dispatcher_client.h" +#include "ui/wm/public/drag_drop_client.h" + +#if defined(OS_WIN) +#include "ui/base/win/internal_constants.h" +#include "ui/views/controls/menu/menu_message_pump_dispatcher_win.h" +#include "ui/views/win/hwnd_util.h" +#else +#include "ui/views/controls/menu/menu_event_dispatcher_linux.h" +#endif + +using aura::client::ScreenPositionClient; + +namespace views { + +namespace { + +aura::Window* GetOwnerRootWindow(views::Widget* owner) { + return owner ? owner->GetNativeWindow()->GetRootWindow() : NULL; +} + +// ActivationChangeObserverImpl is used to observe activation changes and close +// the menu. Additionally it listens for the root window to be destroyed and +// cancel the menu as well. +class ActivationChangeObserverImpl + : public aura::client::ActivationChangeObserver, + public aura::WindowObserver, + public ui::EventHandler { + public: + ActivationChangeObserverImpl(MenuController* controller, aura::Window* root) + : controller_(controller), root_(root) { + aura::client::GetActivationClient(root_)->AddObserver(this); + root_->AddObserver(this); + root_->AddPreTargetHandler(this); + } + + virtual ~ActivationChangeObserverImpl() { Cleanup(); } + + // aura::client::ActivationChangeObserver: + virtual void OnWindowActivated(aura::Window* gained_active, + aura::Window* lost_active) OVERRIDE { + if (!controller_->drag_in_progress()) + controller_->CancelAll(); + } + + // aura::WindowObserver: + virtual void OnWindowDestroying(aura::Window* window) OVERRIDE { Cleanup(); } + + // ui::EventHandler: + virtual void OnCancelMode(ui::CancelModeEvent* event) OVERRIDE { + controller_->CancelAll(); + } + + private: + void Cleanup() { + if (!root_) + return; + // The ActivationClient may have been destroyed by the time we get here. + aura::client::ActivationClient* client = + aura::client::GetActivationClient(root_); + if (client) + client->RemoveObserver(this); + root_->RemovePreTargetHandler(this); + root_->RemoveObserver(this); + root_ = NULL; + } + + MenuController* controller_; + aura::Window* root_; + + DISALLOW_COPY_AND_ASSIGN(ActivationChangeObserverImpl); +}; + +} // namespace + +// static +MenuMessageLoop* MenuMessageLoop::Create() { + return new MenuMessageLoopAura; +} + +MenuMessageLoopAura::MenuMessageLoopAura() : owner_(NULL) { +} + +MenuMessageLoopAura::~MenuMessageLoopAura() { +} + +void MenuMessageLoopAura::RepostEventToWindow(const ui::LocatedEvent& event, + gfx::NativeWindow window, + const gfx::Point& screen_loc) { + aura::Window* root = window->GetRootWindow(); + ScreenPositionClient* spc = aura::client::GetScreenPositionClient(root); + if (!spc) + return; + + gfx::Point root_loc(screen_loc); + spc->ConvertPointFromScreen(root, &root_loc); + + ui::MouseEvent clone(static_cast<const ui::MouseEvent&>(event)); + clone.set_location(root_loc); + clone.set_root_location(root_loc); + root->GetHost()->dispatcher()->RepostEvent(clone); +} + +void MenuMessageLoopAura::Run(MenuController* controller, + Widget* owner, + bool nested_menu) { + // |owner_| may be NULL. + owner_ = owner; + aura::Window* root = GetOwnerRootWindow(owner_); + // It is possible for the same MenuMessageLoopAura to start a nested + // message-loop while it is already running a nested loop. So make sure the + // quit-closure gets reset to the outer loop's quit-closure once the innermost + // loop terminates. + base::AutoReset<base::Closure> reset_quit_closure(&message_loop_quit_, + base::Closure()); + +#if defined(OS_WIN) + internal::MenuMessagePumpDispatcher nested_dispatcher(controller); + if (root) { + scoped_ptr<ActivationChangeObserverImpl> observer; + if (!nested_menu) + observer.reset(new ActivationChangeObserverImpl(controller, root)); + aura::client::DispatcherRunLoop run_loop( + aura::client::GetDispatcherClient(root), &nested_dispatcher); + message_loop_quit_ = run_loop.QuitClosure(); + run_loop.Run(); + } else { + base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); + base::MessageLoop::ScopedNestableTaskAllower allow(loop); + base::RunLoop run_loop(&nested_dispatcher); + message_loop_quit_ = run_loop.QuitClosure(); + run_loop.Run(); + } +#else + internal::MenuEventDispatcher event_dispatcher(controller); + scoped_ptr<ui::ScopedEventDispatcher> old_dispatcher = + nested_dispatcher_.Pass(); + if (ui::PlatformEventSource::GetInstance()) { + nested_dispatcher_ = + ui::PlatformEventSource::GetInstance()->OverrideDispatcher( + &event_dispatcher); + } + if (root) { + scoped_ptr<ActivationChangeObserverImpl> observer; + if (!nested_menu) + observer.reset(new ActivationChangeObserverImpl(controller, root)); + aura::client::DispatcherRunLoop run_loop( + aura::client::GetDispatcherClient(root), NULL); + message_loop_quit_ = run_loop.QuitClosure(); + run_loop.Run(); + } else { + base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); + base::MessageLoop::ScopedNestableTaskAllower allow(loop); + base::RunLoop run_loop; + message_loop_quit_ = run_loop.QuitClosure(); + run_loop.Run(); + } + nested_dispatcher_ = old_dispatcher.Pass(); +#endif +} + +bool MenuMessageLoopAura::ShouldQuitNow() const { + aura::Window* root = GetOwnerRootWindow(owner_); + return !aura::client::GetDragDropClient(root) || + !aura::client::GetDragDropClient(root)->IsDragDropInProgress(); +} + +void MenuMessageLoopAura::QuitNow() { + CHECK(!message_loop_quit_.is_null()); + message_loop_quit_.Run(); + // Restore the previous dispatcher. + nested_dispatcher_.reset(); +} + +void MenuMessageLoopAura::ClearOwner() { + owner_ = NULL; +} + +} // namespace views diff --git a/chromium/ui/views/controls/menu/menu_message_loop_aura.h b/chromium/ui/views/controls/menu/menu_message_loop_aura.h new file mode 100644 index 00000000000..49e8a392827 --- /dev/null +++ b/chromium/ui/views/controls/menu/menu_message_loop_aura.h @@ -0,0 +1,52 @@ +// Copyright 2014 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_CONTROLS_MENU_MENU_MESSAGE_LOOP_AURA_H_ +#define UI_VIEWS_CONTROLS_MENU_MENU_MESSAGE_LOOP_AURA_H_ + +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "ui/views/controls/menu/menu_message_loop.h" + +namespace base { +class MessagePumpDispatcher; +} + +namespace ui { +class ScopedEventDispatcher; +} + +namespace views { + +class MenuMessageLoopAura : public MenuMessageLoop { + public: + MenuMessageLoopAura(); + virtual ~MenuMessageLoopAura(); + + // Overridden from MenuMessageLoop: + virtual void Run(MenuController* controller, + Widget* owner, + bool nested_menu) OVERRIDE; + virtual bool ShouldQuitNow() const OVERRIDE; + virtual void QuitNow() OVERRIDE; + virtual void RepostEventToWindow(const ui::LocatedEvent& event, + gfx::NativeWindow window, + const gfx::Point& screen_loc) OVERRIDE; + virtual void ClearOwner() OVERRIDE; + + private: + // Owner of child windows. + // WARNING: this may be NULL. + Widget* owner_; + + scoped_ptr<ui::ScopedEventDispatcher> nested_dispatcher_; + base::Closure message_loop_quit_; + + DISALLOW_COPY_AND_ASSIGN(MenuMessageLoopAura); +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_MENU_MENU_MESSAGE_LOOP_AURA_H_ diff --git a/chromium/ui/views/controls/menu/menu_message_loop_mac.cc b/chromium/ui/views/controls/menu/menu_message_loop_mac.cc new file mode 100644 index 00000000000..b1c10cca7d8 --- /dev/null +++ b/chromium/ui/views/controls/menu/menu_message_loop_mac.cc @@ -0,0 +1,51 @@ +// Copyright 2014 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/controls/menu/menu_message_loop_mac.h" + +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "ui/gfx/geometry/point.h" + +namespace views { + +// static +MenuMessageLoop* MenuMessageLoop::Create() { + return new MenuMessageLoopMac; +} + +MenuMessageLoopMac::MenuMessageLoopMac() { +} + +MenuMessageLoopMac::~MenuMessageLoopMac() { +} + +void MenuMessageLoopMac::RepostEventToWindow(const ui::LocatedEvent& event, + gfx::NativeWindow window, + const gfx::Point& screen_loc) { + NOTIMPLEMENTED(); +} + +void MenuMessageLoopMac::Run(MenuController* controller, + Widget* owner, + bool nested_menu) { + base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); + base::MessageLoop::ScopedNestableTaskAllower allow(loop); + base::RunLoop run_loop; + run_loop.Run(); +} + +bool MenuMessageLoopMac::ShouldQuitNow() const { + return true; +} + +void MenuMessageLoopMac::QuitNow() { + base::MessageLoop::current()->QuitNow(); +} + +void MenuMessageLoopMac::ClearOwner() { +} + +} // namespace views diff --git a/chromium/ui/views/controls/menu/menu_message_loop_mac.h b/chromium/ui/views/controls/menu/menu_message_loop_mac.h new file mode 100644 index 00000000000..b77d263ffbb --- /dev/null +++ b/chromium/ui/views/controls/menu/menu_message_loop_mac.h @@ -0,0 +1,35 @@ +// Copyright 2014 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_CONTROLS_MENU_MENU_MESSAGE_LOOP_MAC_H_ +#define UI_VIEWS_CONTROLS_MENU_MENU_MESSAGE_LOOP_MAC_H_ + +#include "base/compiler_specific.h" +#include "ui/views/controls/menu/menu_message_loop.h" + +namespace views { + +class MenuMessageLoopMac : public MenuMessageLoop { + public: + MenuMessageLoopMac(); + virtual ~MenuMessageLoopMac(); + + // Overridden from MenuMessageLoop: + virtual void Run(MenuController* controller, + Widget* owner, + bool nested_menu) OVERRIDE; + virtual bool ShouldQuitNow() const OVERRIDE; + virtual void QuitNow() OVERRIDE; + virtual void RepostEventToWindow(const ui::LocatedEvent& event, + gfx::NativeWindow window, + const gfx::Point& screen_loc) OVERRIDE; + virtual void ClearOwner() OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(MenuMessageLoopMac); +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_MENU_MENU_MESSAGE_LOOP_MAC_H_ diff --git a/chromium/ui/views/controls/menu/menu_message_pump_dispatcher_win.cc b/chromium/ui/views/controls/menu/menu_message_pump_dispatcher_win.cc new file mode 100644 index 00000000000..8f905d029ae --- /dev/null +++ b/chromium/ui/views/controls/menu/menu_message_pump_dispatcher_win.cc @@ -0,0 +1,95 @@ +// Copyright 2014 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/controls/menu/menu_message_pump_dispatcher_win.h" + +#include <windowsx.h> + +#include "ui/events/event_utils.h" +#include "ui/events/keycodes/keyboard_code_conversion.h" +#include "ui/events/keycodes/keyboard_codes.h" +#include "ui/views/controls/menu/menu_controller.h" +#include "ui/views/controls/menu/menu_item_view.h" + +namespace views { +namespace internal { + +MenuMessagePumpDispatcher::MenuMessagePumpDispatcher(MenuController* controller) + : menu_controller_(controller) {} + +MenuMessagePumpDispatcher::~MenuMessagePumpDispatcher() {} + +uint32_t MenuMessagePumpDispatcher::Dispatch(const MSG& msg) { + DCHECK(menu_controller_->IsBlockingRun()); + + bool should_quit = false; + bool should_perform_default = true; + if (menu_controller_->exit_type() == MenuController::EXIT_ALL || + menu_controller_->exit_type() == MenuController::EXIT_DESTROYED) { + should_quit = true; + } else { + // NOTE: we don't get WM_ACTIVATE or anything else interesting in here. + switch (msg.message) { + case WM_CONTEXTMENU: { + MenuItemView* item = menu_controller_->pending_state_.item; + if (item && item->GetRootMenuItem() != item) { + gfx::Point screen_loc(0, item->height()); + View::ConvertPointToScreen(item, &screen_loc); + ui::MenuSourceType source_type = ui::MENU_SOURCE_MOUSE; + if (GET_X_LPARAM(msg.lParam) == -1 && GET_Y_LPARAM(msg.lParam) == -1) + source_type = ui::MENU_SOURCE_KEYBOARD; + item->GetDelegate()->ShowContextMenu( + item, item->GetCommand(), screen_loc, source_type); + } + should_quit = false; + should_perform_default = false; + break; + } + + // NOTE: focus wasn't changed when the menu was shown. As such, don't + // dispatch key events otherwise the focused window will get the events. + case WM_KEYDOWN: { + bool result = + menu_controller_->OnKeyDown(ui::KeyboardCodeFromNative(msg)); + TranslateMessage(&msg); + should_perform_default = false; + should_quit = !result; + break; + } + case WM_CHAR: { + should_quit = menu_controller_->SelectByChar( + static_cast<base::char16>(msg.wParam)); + should_perform_default = false; + break; + } + case WM_KEYUP: + case WM_SYSKEYUP: + // We may have been shown on a system key, as such don't do anything + // here. If another system key is pushed we'll get a WM_SYSKEYDOWN and + // close the menu. + should_quit = false; + should_perform_default = false; + break; + + case WM_CANCELMODE: + case WM_SYSKEYDOWN: + // Exit immediately on system keys. + menu_controller_->Cancel(MenuController::EXIT_ALL); + should_quit = true; + should_perform_default = false; + break; + + default: + break; + } + } + + if (should_quit || menu_controller_->exit_type() != MenuController::EXIT_NONE) + menu_controller_->TerminateNestedMessageLoop(); + return should_perform_default ? POST_DISPATCH_PERFORM_DEFAULT + : POST_DISPATCH_NONE; +} + +} // namespace internal +} // namespace views diff --git a/chromium/ui/views/controls/menu/menu_message_pump_dispatcher_win.h b/chromium/ui/views/controls/menu/menu_message_pump_dispatcher_win.h new file mode 100644 index 00000000000..31f1b369610 --- /dev/null +++ b/chromium/ui/views/controls/menu/menu_message_pump_dispatcher_win.h @@ -0,0 +1,36 @@ +// Copyright 2014 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_CONTROLS_MENU_MENU_MESSAGE_PUMP_DISPATCHER_WIN_H_ +#define UI_VIEWS_CONTROLS_MENU_MENU_MESSAGE_PUMP_DISPATCHER_WIN_H_ + +#include "base/macros.h" +#include "base/message_loop/message_pump_dispatcher.h" + +namespace views { + +class MenuController; + +namespace internal { + +// A message-pump dispatcher object used to dispatch events from the nested +// message-loop initiated by the MenuController. +class MenuMessagePumpDispatcher : public base::MessagePumpDispatcher { + public: + explicit MenuMessagePumpDispatcher(MenuController* menu_controller); + virtual ~MenuMessagePumpDispatcher(); + + private: + // base::MessagePumpDispatcher: + virtual uint32_t Dispatch(const base::NativeEvent& event) OVERRIDE; + + MenuController* menu_controller_; + + DISALLOW_COPY_AND_ASSIGN(MenuMessagePumpDispatcher); +}; + +} // namespace internal +} // namespace views + +#endif // UI_VIEWS_CONTROLS_MENU_MENU_MESSAGE_PUMP_DISPATCHER_WIN_H_ diff --git a/chromium/ui/views/controls/menu/menu_model_adapter.cc b/chromium/ui/views/controls/menu/menu_model_adapter.cc index 07f1f403fd8..55c5d7d21d9 100644 --- a/chromium/ui/views/controls/menu/menu_model_adapter.cc +++ b/chromium/ui/views/controls/menu/menu_model_adapter.cc @@ -8,6 +8,7 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/base/models/menu_model.h" #include "ui/gfx/image/image.h" +#include "ui/views/controls/menu/menu_item_view.h" #include "ui/views/controls/menu/submenu_view.h" #include "ui/views/views_delegate.h" @@ -59,7 +60,7 @@ MenuItemView* MenuModelAdapter::AddMenuItemFromModelAt(ui::MenuModel* model, int item_id) { gfx::Image icon; model->GetIconAt(model_index, &icon); - string16 label, sublabel, minor_text; + base::string16 label, sublabel, minor_text; ui::MenuSeparatorType separator_style = ui::NORMAL_SEPARATOR; MenuItemView::Type type; ui::MenuModel::ItemType menu_type = model->GetTypeAt(model_index); @@ -161,7 +162,7 @@ bool MenuModelAdapter::IsTriggerableEvent(MenuItemView* source, } bool MenuModelAdapter::GetAccelerator(int id, - ui::Accelerator* accelerator) { + ui::Accelerator* accelerator) const { ui::MenuModel* model = menu_model_; int index = 0; if (ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index)) @@ -171,27 +172,27 @@ bool MenuModelAdapter::GetAccelerator(int id, return false; } -string16 MenuModelAdapter::GetLabel(int id) const { +base::string16 MenuModelAdapter::GetLabel(int id) const { ui::MenuModel* model = menu_model_; int index = 0; if (ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index)) return model->GetLabelAt(index); NOTREACHED(); - return string16(); + return base::string16(); } -const gfx::Font* MenuModelAdapter::GetLabelFont(int id) const { +const gfx::FontList* MenuModelAdapter::GetLabelFontList(int id) const { ui::MenuModel* model = menu_model_; int index = 0; if (ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index)) { - const gfx::Font* font = model->GetLabelFontAt(index); - if (font) - return font; + const gfx::FontList* font_list = model->GetLabelFontListAt(index); + if (font_list) + return font_list; } // This line may be reached for the empty menu item. - return MenuDelegate::GetLabelFont(id); + return MenuDelegate::GetLabelFontList(id); } bool MenuModelAdapter::IsCommandEnabled(int id) const { @@ -204,6 +205,16 @@ bool MenuModelAdapter::IsCommandEnabled(int id) const { return false; } +bool MenuModelAdapter::IsCommandVisible(int id) const { + ui::MenuModel* model = menu_model_; + int index = 0; + if (ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index)) + return model->IsVisibleAt(index); + + NOTREACHED(); + return false; +} + bool MenuModelAdapter::IsItemChecked(int id) const { ui::MenuModel* model = menu_model_; int index = 0; @@ -264,9 +275,6 @@ void MenuModelAdapter::BuildMenuImpl(MenuItemView* menu, ui::MenuModel* model) { for (int i = 0; i < item_count; ++i) { MenuItemView* item = AppendMenuItem(menu, model, i); - if (item) - item->SetVisible(model->IsVisibleAt(i)); - if (model->GetTypeAt(i) == ui::MenuModel::TYPE_SUBMENU) { DCHECK(item); DCHECK_EQ(MenuItemView::SUBMENU, item->GetType()); diff --git a/chromium/ui/views/controls/menu/menu_model_adapter.h b/chromium/ui/views/controls/menu/menu_model_adapter.h index 4db9dbe2cf9..353d010c533 100644 --- a/chromium/ui/views/controls/menu/menu_model_adapter.h +++ b/chromium/ui/views/controls/menu/menu_model_adapter.h @@ -67,10 +67,11 @@ class VIEWS_EXPORT MenuModelAdapter : public MenuDelegate { virtual bool IsTriggerableEvent(MenuItemView* source, const ui::Event& e) OVERRIDE; virtual bool GetAccelerator(int id, - ui::Accelerator* accelerator) OVERRIDE; - virtual string16 GetLabel(int id) const OVERRIDE; - virtual const gfx::Font* GetLabelFont(int id) const OVERRIDE; + ui::Accelerator* accelerator) const OVERRIDE; + virtual base::string16 GetLabel(int id) const OVERRIDE; + virtual const gfx::FontList* GetLabelFontList(int id) const OVERRIDE; virtual bool IsCommandEnabled(int id) const OVERRIDE; + virtual bool IsCommandVisible(int id) const OVERRIDE; virtual bool IsItemChecked(int id) const OVERRIDE; virtual void SelectionChanged(MenuItemView* menu) OVERRIDE; virtual void WillShowMenu(MenuItemView* menu) OVERRIDE; diff --git a/chromium/ui/views/controls/menu/menu_model_adapter_unittest.cc b/chromium/ui/views/controls/menu/menu_model_adapter_unittest.cc index 6ce3a376270..cba4d2fa7fb 100644 --- a/chromium/ui/views/controls/menu/menu_model_adapter_unittest.cc +++ b/chromium/ui/views/controls/menu/menu_model_adapter_unittest.cc @@ -2,12 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "ui/views/controls/menu/menu_model_adapter.h" + #include "base/strings/utf_string_conversions.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/models/menu_model.h" #include "ui/base/models/menu_model_delegate.h" #include "ui/views/controls/menu/menu_item_view.h" -#include "ui/views/controls/menu/menu_model_adapter.h" #include "ui/views/controls/menu/menu_runner.h" #include "ui/views/controls/menu/submenu_view.h" #include "ui/views/test/views_test_base.h" @@ -51,7 +52,7 @@ class MenuModelBase : public ui::MenuModel { return index + command_id_base_; } - virtual string16 GetLabelAt(int index) const OVERRIDE { + virtual base::string16 GetLabelAt(int index) const OVERRIDE { return items_[index].label; } @@ -59,7 +60,7 @@ class MenuModelBase : public ui::MenuModel { return false; } - virtual const gfx::Font* GetLabelFontAt(int index) const OVERRIDE { + virtual const gfx::FontList* GetLabelFontListAt(int index) const OVERRIDE { return NULL; } @@ -128,12 +129,12 @@ class MenuModelBase : public ui::MenuModel { const std::string& item_label, ui::MenuModel* item_submenu) : type(item_type), - label(ASCIIToUTF16(item_label)), + label(base::ASCIIToUTF16(item_label)), submenu(item_submenu) { } ItemType type; - string16 label; + base::string16 label; ui::MenuModel* submenu; }; diff --git a/chromium/ui/views/controls/menu/menu_runner.cc b/chromium/ui/views/controls/menu/menu_runner.cc index e254dbffc55..ca36d026433 100644 --- a/chromium/ui/views/controls/menu/menu_runner.cc +++ b/chromium/ui/views/controls/menu/menu_runner.cc @@ -12,6 +12,7 @@ #include "ui/views/controls/menu/menu_controller.h" #include "ui/views/controls/menu/menu_controller_delegate.h" #include "ui/views/controls/menu/menu_delegate.h" +#include "ui/views/controls/menu/menu_item_view.h" #include "ui/views/controls/menu/menu_model_adapter.h" #include "ui/views/controls/menu/menu_runner_handler.h" #include "ui/views/widget/widget.h" @@ -43,7 +44,7 @@ class MenuRunnerImpl : public internal::MenuControllerDelegate { MenuRunner::RunResult RunMenuAt(Widget* parent, MenuButton* button, const gfx::Rect& bounds, - MenuItemView::AnchorPosition anchor, + MenuAnchorPosition anchor, int32 types) WARN_UNUSED_RESULT; void Cancel(); @@ -140,12 +141,11 @@ void MenuRunnerImpl::Release() { } } -MenuRunner::RunResult MenuRunnerImpl::RunMenuAt( - Widget* parent, - MenuButton* button, - const gfx::Rect& bounds, - MenuItemView::AnchorPosition anchor, - int32 types) { +MenuRunner::RunResult MenuRunnerImpl::RunMenuAt(Widget* parent, + MenuButton* button, + const gfx::Rect& bounds, + MenuAnchorPosition anchor, + int32 types) { closing_event_time_ = base::TimeDelta(); if (running_) { // Ignore requests to show the menu while it's already showing. MenuItemView @@ -186,7 +186,7 @@ MenuRunner::RunResult MenuRunnerImpl::RunMenuAt( controller = new MenuController(theme, !for_drop_, this); owns_controller_ = true; } - controller->set_accept_on_f4((types & MenuRunner::COMBOBOX) != 0); + controller->set_is_combobox((types & MenuRunner::COMBOBOX) != 0); controller_ = controller; menu_->set_controller(controller_); menu_->PrepareForRun(owns_controller_, @@ -278,16 +278,6 @@ bool MenuRunnerImpl::ShouldShowMnemonics(MenuButton* button) { return show_mnemonics; } -// In theory we could implement this every where, but for now we're only -// implementing it on aura. -#if !defined(USE_AURA) -// static -DisplayChangeListener* DisplayChangeListener::Create(Widget* widget, - MenuRunner* runner) { - return NULL; -} -#endif - } // namespace internal MenuRunner::MenuRunner(ui::MenuModel* menu_model) @@ -310,7 +300,7 @@ MenuItemView* MenuRunner::GetMenu() { MenuRunner::RunResult MenuRunner::RunMenuAt(Widget* parent, MenuButton* button, const gfx::Rect& bounds, - MenuItemView::AnchorPosition anchor, + MenuAnchorPosition anchor, ui::MenuSourceType source_type, int32 types) { if (runner_handler_.get()) { @@ -331,11 +321,11 @@ MenuRunner::RunResult MenuRunner::RunMenuAt(Widget* parent, case ui::MENU_SOURCE_NONE: case ui::MENU_SOURCE_KEYBOARD: case ui::MENU_SOURCE_MOUSE: - anchor = MenuItemView::TOPLEFT; + anchor = MENU_ANCHOR_TOPLEFT; break; case ui::MENU_SOURCE_TOUCH: case ui::MENU_SOURCE_TOUCH_EDIT_MENU: - anchor = MenuItemView::BOTTOMCENTER; + anchor = MENU_ANCHOR_BOTTOMCENTER; break; default: break; diff --git a/chromium/ui/views/controls/menu/menu_runner.h b/chromium/ui/views/controls/menu/menu_runner.h index 6b46362f18e..824afdf8d4b 100644 --- a/chromium/ui/views/controls/menu/menu_runner.h +++ b/chromium/ui/views/controls/menu/menu_runner.h @@ -8,7 +8,17 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" -#include "ui/views/controls/menu/menu_item_view.h" +#include "ui/base/ui_base_types.h" +#include "ui/views/controls/menu/menu_types.h" +#include "ui/views/views_export.h" + +namespace base { +class TimeDelta; +} + +namespace gfx { +class Rect; +} namespace ui { class MenuModel; @@ -17,6 +27,7 @@ class MenuModel; namespace views { class MenuButton; +class MenuItemView; class MenuModelAdapter; class MenuRunnerHandler; class Widget; @@ -103,7 +114,7 @@ class VIEWS_EXPORT MenuRunner { RunResult RunMenuAt(Widget* parent, MenuButton* button, const gfx::Rect& bounds, - MenuItemView::AnchorPosition anchor, + MenuAnchorPosition anchor, ui::MenuSourceType source_type, int32 types) WARN_UNUSED_RESULT; diff --git a/chromium/ui/views/controls/menu/menu_runner_handler.h b/chromium/ui/views/controls/menu/menu_runner_handler.h index 46f801af685..47d75c80fce 100644 --- a/chromium/ui/views/controls/menu/menu_runner_handler.h +++ b/chromium/ui/views/controls/menu/menu_runner_handler.h @@ -6,11 +6,11 @@ #define UI_VIEWS_CONTROLS_MENU_MENU_RUNNER_HANDLER_H_ #include "base/basictypes.h" -#include "ui/views/controls/menu/menu_item_view.h" namespace views { class MenuButton; +class MenuItemView; class Widget; // Used internally by MenuRunner to show the menu. Can be set in tests (see @@ -21,7 +21,7 @@ class VIEWS_EXPORT MenuRunnerHandler { virtual MenuRunner::RunResult RunMenuAt(Widget* parent, MenuButton* button, const gfx::Rect& bounds, - MenuItemView::AnchorPosition anchor, + MenuAnchorPosition anchor, ui::MenuSourceType source_type, int32 types) = 0; }; diff --git a/chromium/ui/views/controls/menu/menu_scroll_view_container.cc b/chromium/ui/views/controls/menu/menu_scroll_view_container.cc index 7b8edbecab5..451bd1537ad 100644 --- a/chromium/ui/views/controls/menu/menu_scroll_view_container.cc +++ b/chromium/ui/views/controls/menu/menu_scroll_view_container.cc @@ -6,8 +6,9 @@ #include "third_party/skia/include/core/SkPaint.h" #include "third_party/skia/include/core/SkPath.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/gfx/canvas.h" +#include "ui/native_theme/native_theme_aura.h" #include "ui/views/border.h" #include "ui/views/bubble/bubble_border.h" #include "ui/views/controls/menu/menu_config.h" @@ -16,10 +17,6 @@ #include "ui/views/controls/menu/submenu_view.h" #include "ui/views/round_rect_painter.h" -#if defined(USE_AURA) -#include "ui/native_theme/native_theme_aura.h" -#endif - using ui::NativeTheme; namespace views { @@ -43,7 +40,7 @@ class MenuScrollButton : public View { pref_height_(MenuItemView::pref_menu_height()) { } - virtual gfx::Size GetPreferredSize() OVERRIDE { + virtual gfx::Size GetPreferredSize() const OVERRIDE { return gfx::Size( host_->GetMenuItem()->GetMenuConfig().scroll_arrow_height * 2 - 1, pref_height_); @@ -246,7 +243,7 @@ void MenuScrollViewContainer::Layout() { scroll_view_->Layout(); } -gfx::Size MenuScrollViewContainer::GetPreferredSize() { +gfx::Size MenuScrollViewContainer::GetPreferredSize() const { gfx::Size prefsize = scroll_view_->GetContents()->GetPreferredSize(); gfx::Insets insets = GetInsets(); prefsize.Enlarge(insets.width(), insets.height()); @@ -254,15 +251,15 @@ gfx::Size MenuScrollViewContainer::GetPreferredSize() { } void MenuScrollViewContainer::GetAccessibleState( - ui::AccessibleViewState* state) { + ui::AXViewState* state) { // Get the name from the submenu view. content_view_->GetAccessibleState(state); // Now change the role. - state->role = ui::AccessibilityTypes::ROLE_MENUBAR; + state->role = ui::AX_ROLE_MENU_BAR; // Some AT (like NVDA) will not process focus events on menu item children // unless a parent claims to be focused. - state->state = ui::AccessibilityTypes::STATE_FOCUSED; + state->AddStateFlag(ui::AX_STATE_FOCUSED); } void MenuScrollViewContainer::OnBoundsChanged( @@ -284,7 +281,7 @@ void MenuScrollViewContainer::CreateDefaultBorder() { int padding = menu_config.corner_radius > 0 ? kBorderPaddingDueToRoundedCorners : 0; -#if defined(USE_AURA) +#if defined(USE_AURA) && !(defined(OS_LINUX) && !defined(OS_CHROMEOS)) if (menu_config.native_theme == ui::NativeThemeAura::instance()) { // In case of NativeThemeAura the border gets drawn with the shadow. // Furthermore no additional padding is wanted. @@ -299,13 +296,14 @@ void MenuScrollViewContainer::CreateDefaultBorder() { int right = menu_config.menu_horizontal_border_size + padding; if (use_border) { - set_border(views::Border::CreateBorderPainter( - new views::RoundRectPainter(menu_config.native_theme->GetSystemColor( + SetBorder(views::Border::CreateBorderPainter( + new views::RoundRectPainter( + menu_config.native_theme->GetSystemColor( ui::NativeTheme::kColorId_MenuBorderColor), menu_config.corner_radius), - gfx::Insets(top, left, bottom, right))); + gfx::Insets(top, left, bottom, right))); } else { - set_border(Border::CreateEmptyBorder(top, left, bottom, right)); + SetBorder(Border::CreateEmptyBorder(top, left, bottom, right)); } } @@ -313,21 +311,20 @@ void MenuScrollViewContainer::CreateBubbleBorder() { bubble_border_ = new BubbleBorder(arrow_, BubbleBorder::SMALL_SHADOW, SK_ColorWHITE); - set_border(bubble_border_); + SetBorder(scoped_ptr<Border>(bubble_border_)); set_background(new BubbleBackground(bubble_border_)); } -BubbleBorder::Arrow -MenuScrollViewContainer::BubbleBorderTypeFromAnchor( - MenuItemView::AnchorPosition anchor) { +BubbleBorder::Arrow MenuScrollViewContainer::BubbleBorderTypeFromAnchor( + MenuAnchorPosition anchor) { switch (anchor) { - case views::MenuItemView::BUBBLE_LEFT: + case MENU_ANCHOR_BUBBLE_LEFT: return BubbleBorder::RIGHT_CENTER; - case views::MenuItemView::BUBBLE_RIGHT: + case MENU_ANCHOR_BUBBLE_RIGHT: return BubbleBorder::LEFT_CENTER; - case views::MenuItemView::BUBBLE_ABOVE: + case MENU_ANCHOR_BUBBLE_ABOVE: return BubbleBorder::BOTTOM_CENTER; - case views::MenuItemView::BUBBLE_BELOW: + case MENU_ANCHOR_BUBBLE_BELOW: return BubbleBorder::TOP_CENTER; default: return BubbleBorder::NONE; diff --git a/chromium/ui/views/controls/menu/menu_scroll_view_container.h b/chromium/ui/views/controls/menu/menu_scroll_view_container.h index 83baa2a3939..dc7c1acf164 100644 --- a/chromium/ui/views/controls/menu/menu_scroll_view_container.h +++ b/chromium/ui/views/controls/menu/menu_scroll_view_container.h @@ -7,7 +7,7 @@ #include "ui/views/view.h" #include "ui/views/bubble/bubble_border.h" -#include "ui/views/controls/menu/menu_item_view.h" +#include "ui/views/controls/menu/menu_types.h" namespace views { @@ -33,8 +33,8 @@ class MenuScrollViewContainer : public View { // View overrides. virtual void OnPaintBackground(gfx::Canvas* canvas) OVERRIDE; virtual void Layout() OVERRIDE; - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; protected: // View override. @@ -47,8 +47,7 @@ class MenuScrollViewContainer : public View { // Create the bubble border. void CreateBubbleBorder(); - BubbleBorder::Arrow BubbleBorderTypeFromAnchor( - MenuItemView::AnchorPosition anchor); + BubbleBorder::Arrow BubbleBorderTypeFromAnchor(MenuAnchorPosition anchor); class MenuScrollView; @@ -65,7 +64,7 @@ class MenuScrollViewContainer : public View { // If set the currently set border is a bubble border. BubbleBorder::Arrow arrow_; - // The currently set border. + // Weak reference to the currently set border. BubbleBorder* bubble_border_; DISALLOW_COPY_AND_ASSIGN(MenuScrollViewContainer); diff --git a/chromium/ui/views/controls/menu/menu_separator.h b/chromium/ui/views/controls/menu/menu_separator.h index 4098ae8f0ae..da459e2b07a 100644 --- a/chromium/ui/views/controls/menu/menu_separator.h +++ b/chromium/ui/views/controls/menu/menu_separator.h @@ -21,13 +21,11 @@ class MenuSeparator : public View { // View overrides. virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; private: -#if defined(USE_AURA) void OnPaintAura(gfx::Canvas* canvas); - gfx::Size GetPreferredSizeAura(); -#endif + gfx::Size GetPreferredSizeAura() const; // The type of the separator. const ui::MenuSeparatorType type_; diff --git a/chromium/ui/views/controls/menu/menu_separator_views.cc b/chromium/ui/views/controls/menu/menu_separator_views.cc index affbae48966..34f85453501 100644 --- a/chromium/ui/views/controls/menu/menu_separator_views.cc +++ b/chromium/ui/views/controls/menu/menu_separator_views.cc @@ -23,7 +23,7 @@ void MenuSeparator::OnPaint(gfx::Canvas* canvas) { OnPaintAura(canvas); } -gfx::Size MenuSeparator::GetPreferredSize() { +gfx::Size MenuSeparator::GetPreferredSize() const { return GetPreferredSizeAura(); } #endif @@ -47,7 +47,7 @@ void MenuSeparator::OnPaintAura(gfx::Canvas* canvas) { ui::NativeTheme::kColorId_MenuSeparatorColor)); } -gfx::Size MenuSeparator::GetPreferredSizeAura() { +gfx::Size MenuSeparator::GetPreferredSizeAura() const { const MenuConfig& menu_config = parent_menu_item_->GetMenuConfig(); int height = menu_config.separator_height; switch(type_) { diff --git a/chromium/ui/views/controls/menu/menu_separator_win.cc b/chromium/ui/views/controls/menu/menu_separator_win.cc index 6f95334790a..ccbcaed263e 100644 --- a/chromium/ui/views/controls/menu/menu_separator_win.cc +++ b/chromium/ui/views/controls/menu/menu_separator_win.cc @@ -11,24 +11,19 @@ #include "ui/gfx/canvas.h" #include "ui/gfx/rect.h" #include "ui/native_theme/native_theme.h" +#include "ui/native_theme/native_theme_aura.h" #include "ui/views/controls/menu/menu_config.h" #include "ui/views/controls/menu/menu_item_view.h" -#if defined(USE_AURA) -#include "ui/native_theme/native_theme_aura.h" -#endif - namespace views { void MenuSeparator::OnPaint(gfx::Canvas* canvas) { const MenuConfig& config = parent_menu_item_->GetMenuConfig(); -#if defined(USE_AURA) if (config.native_theme == ui::NativeThemeAura::instance()) { OnPaintAura(canvas); return; } -#endif int start_x = 0; if (config.render_gutter) { @@ -52,13 +47,11 @@ void MenuSeparator::OnPaint(gfx::Canvas* canvas) { ui::NativeTheme::kNormal, separator_bounds, extra); } -gfx::Size MenuSeparator::GetPreferredSize() { +gfx::Size MenuSeparator::GetPreferredSize() const { const MenuConfig& config = parent_menu_item_->GetMenuConfig(); -#if defined(USE_AURA) if (config.native_theme == ui::NativeThemeAura::instance()) return GetPreferredSizeAura(); -#endif return gfx::Size(10, // Just in case we're the only item in a menu. config.separator_height); diff --git a/chromium/ui/views/controls/menu/menu_types.h b/chromium/ui/views/controls/menu/menu_types.h new file mode 100644 index 00000000000..160dd5ae3d1 --- /dev/null +++ b/chromium/ui/views/controls/menu/menu_types.h @@ -0,0 +1,26 @@ +// Copyright 2014 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_CONTROLS_MENU_MENU_TYPES_H_ +#define UI_VIEWS_CONTROLS_MENU_MENU_TYPES_H_ + +namespace views { + +// Where a popup menu should be anchored to for non-RTL languages. The opposite +// position will be used if base::i18n:IsRTL() is true. The BUBBLE flags are +// used when the menu should get enclosed by a bubble. Note that BUBBLE flags +// should only be used with menus which have no children. +enum MenuAnchorPosition { + MENU_ANCHOR_TOPLEFT, + MENU_ANCHOR_TOPRIGHT, + MENU_ANCHOR_BOTTOMCENTER, + MENU_ANCHOR_BUBBLE_LEFT, + MENU_ANCHOR_BUBBLE_RIGHT, + MENU_ANCHOR_BUBBLE_ABOVE, + MENU_ANCHOR_BUBBLE_BELOW +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_MENU_MENU_TYPES_H_ diff --git a/chromium/ui/views/controls/menu/menu_win.cc b/chromium/ui/views/controls/menu/menu_win.cc deleted file mode 100644 index d88659ff7d4..00000000000 --- a/chromium/ui/views/controls/menu/menu_win.cc +++ /dev/null @@ -1,572 +0,0 @@ -// 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/controls/menu/menu_win.h" - -#include <string> - -#include "base/logging.h" -#include "base/stl_util.h" -#include "base/strings/string_util.h" -#include "ui/base/accelerators/accelerator.h" -#include "ui/base/l10n/l10n_util.h" -#include "ui/base/l10n/l10n_util_win.h" -#include "ui/events/keycodes/keyboard_codes.h" -#include "ui/gfx/canvas.h" -#include "ui/gfx/font.h" -#include "ui/gfx/rect.h" -#include "ui/gfx/win/window_impl.h" -#include "ui/views/layout/layout_constants.h" - -namespace views { - -// The width of an icon, including the pixels between the icon and -// the item label. -const int kIconWidth = 23; -// Margins between the top of the item and the label. -const int kItemTopMargin = 3; -// Margins between the bottom of the item and the label. -const int kItemBottomMargin = 4; -// Margins between the left of the item and the icon. -const int kItemLeftMargin = 4; -// The width for displaying the sub-menu arrow. -const int kArrowWidth = 10; - -// Current active MenuHostWindow. If NULL, no menu is active. -static MenuHostWindow* active_host_window = NULL; - -// The data of menu items needed to display. -struct MenuWin::ItemData { - string16 label; - gfx::ImageSkia icon; - bool submenu; -}; - -namespace { - -static int ChromeGetMenuItemID(HMENU hMenu, int pos) { - // The built-in Windows GetMenuItemID doesn't work for submenus, - // so here's our own implementation. - MENUITEMINFO mii = {0}; - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_ID; - GetMenuItemInfo(hMenu, pos, TRUE, &mii); - return mii.wID; -} - -// MenuHostWindow ------------------------------------------------------------- - -// MenuHostWindow is the HWND the HMENU is parented to. MenuHostWindow is used -// to intercept right clicks on the HMENU and notify the delegate as well as -// for drawing icons. -// -class MenuHostWindow : public gfx::WindowImpl { - public: - MenuHostWindow(MenuWin* menu, HWND parent_window) : menu_(menu) { - int extended_style = 0; - // If the menu needs to be created with a right-to-left UI layout, we must - // set the appropriate RTL flags (such as WS_EX_LAYOUTRTL) property for the - // underlying HWND. - if (menu_->delegate()->IsRightToLeftUILayout()) - extended_style |= l10n_util::GetExtendedStyles(); - set_window_style(WS_CHILD); - set_window_ex_style(extended_style); - Init(parent_window, gfx::Rect()); - } - - ~MenuHostWindow() { - DestroyWindow(hwnd()); - } - - BEGIN_MSG_MAP_EX(MenuHostWindow); - MSG_WM_RBUTTONUP(OnRButtonUp) - MSG_WM_MEASUREITEM(OnMeasureItem) - MSG_WM_DRAWITEM(OnDrawItem) - END_MSG_MAP(); - - private: - // NOTE: I really REALLY tried to use WM_MENURBUTTONUP, but I ran into - // two problems in using it: - // 1. It doesn't contain the coordinates of the mouse. - // 2. It isn't invoked for menuitems representing a submenu that have children - // menu items (not empty). - - void OnRButtonUp(UINT w_param, const CPoint& loc) { - int id; - if (menu_->delegate() && FindMenuIDByLocation(menu_, loc, &id)) - menu_->delegate()->ShowContextMenu(menu_, id, gfx::Point(loc), true); - } - - void OnMeasureItem(WPARAM w_param, MEASUREITEMSTRUCT* lpmis) { - MenuWin::ItemData* data = - reinterpret_cast<MenuWin::ItemData*>(lpmis->itemData); - if (data != NULL) { - gfx::Font font; - lpmis->itemWidth = font.GetStringWidth(data->label) + kIconWidth + - kItemLeftMargin + views::kItemLabelSpacing - - GetSystemMetrics(SM_CXMENUCHECK); - if (data->submenu) - lpmis->itemWidth += kArrowWidth; - // If the label contains an accelerator, make room for tab. - if (data->label.find(L'\t') != string16::npos) - lpmis->itemWidth += font.GetStringWidth(L" "); - lpmis->itemHeight = font.GetHeight() + kItemBottomMargin + kItemTopMargin; - } else { - // Measure separator size. - lpmis->itemHeight = GetSystemMetrics(SM_CYMENU) / 2; - lpmis->itemWidth = 0; - } - } - - void OnDrawItem(UINT wParam, DRAWITEMSTRUCT* lpdis) { - HDC hDC = lpdis->hDC; - COLORREF prev_bg_color, prev_text_color; - - // Set background color and text color - if (lpdis->itemState & ODS_SELECTED) { - prev_bg_color = SetBkColor(hDC, GetSysColor(COLOR_HIGHLIGHT)); - prev_text_color = SetTextColor(hDC, GetSysColor(COLOR_HIGHLIGHTTEXT)); - } else { - prev_bg_color = SetBkColor(hDC, GetSysColor(COLOR_MENU)); - if (lpdis->itemState & ODS_DISABLED) - prev_text_color = SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT)); - else - prev_text_color = SetTextColor(hDC, GetSysColor(COLOR_MENUTEXT)); - } - - if (lpdis->itemData) { - MenuWin::ItemData* data = - reinterpret_cast<MenuWin::ItemData*>(lpdis->itemData); - - // Draw the background. - HBRUSH hbr = CreateSolidBrush(GetBkColor(hDC)); - FillRect(hDC, &lpdis->rcItem, hbr); - DeleteObject(hbr); - - // Draw the label. - RECT rect = lpdis->rcItem; - rect.top += kItemTopMargin; - // Should we add kIconWidth only when icon.width() != 0 ? - rect.left += kItemLeftMargin + kIconWidth; - rect.right -= views::kItemLabelSpacing; - UINT format = DT_TOP | DT_SINGLELINE; - // Check whether the mnemonics should be underlined. - BOOL underline_mnemonics; - SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &underline_mnemonics, 0); - if (!underline_mnemonics) - format |= DT_HIDEPREFIX; - gfx::Font font; - HGDIOBJ old_font = - static_cast<HFONT>(SelectObject(hDC, font.GetNativeFont())); - - // If an accelerator is specified (with a tab delimiting the rest of the - // label from the accelerator), we have to justify the fist part on the - // left and the accelerator on the right. - // TODO(jungshik): This will break in RTL UI. Currently, he/ar use the - // window system UI font and will not hit here. - string16 label = data->label; - string16 accel; - string16::size_type tab_pos = label.find(L'\t'); - if (tab_pos != string16::npos) { - accel = label.substr(tab_pos); - label = label.substr(0, tab_pos); - } - DrawTextEx(hDC, const_cast<wchar_t*>(label.data()), - static_cast<int>(label.size()), &rect, format | DT_LEFT, NULL); - if (!accel.empty()) - DrawTextEx(hDC, const_cast<wchar_t*>(accel.data()), - static_cast<int>(accel.size()), &rect, - format | DT_RIGHT, NULL); - SelectObject(hDC, old_font); - - // Draw the icon after the label, otherwise it would be covered - // by the label. - gfx::ImageSkiaRep icon_image_rep = data->icon.GetRepresentation(1.0f); - if (data->icon.width() != 0 && data->icon.height() != 0) { - gfx::Canvas canvas(icon_image_rep, false); - skia::DrawToNativeContext( - canvas.sk_canvas(), hDC, lpdis->rcItem.left + kItemLeftMargin, - lpdis->rcItem.top + (lpdis->rcItem.bottom - lpdis->rcItem.top - - data->icon.height()) / 2, NULL); - } - - } else { - // Draw the separator - lpdis->rcItem.top += (lpdis->rcItem.bottom - lpdis->rcItem.top) / 3; - DrawEdge(hDC, &lpdis->rcItem, EDGE_ETCHED, BF_TOP); - } - - SetBkColor(hDC, prev_bg_color); - SetTextColor(hDC, prev_text_color); - } - - bool FindMenuIDByLocation(MenuWin* menu, const CPoint& loc, int* id) { - int index = MenuItemFromPoint(NULL, menu->menu_, loc); - if (index != -1) { - *id = ChromeGetMenuItemID(menu->menu_, index); - return true; - } else { - for (std::vector<MenuWin*>::iterator i = menu->submenus_.begin(); - i != menu->submenus_.end(); ++i) { - if (FindMenuIDByLocation(*i, loc, id)) - return true; - } - } - return false; - } - - // The menu that created us. - MenuWin* menu_; - - DISALLOW_COPY_AND_ASSIGN(MenuHostWindow); -}; - -} // namespace - -// static -Menu* Menu::Create(Delegate* delegate, - AnchorPoint anchor, - gfx::NativeView parent) { - return new MenuWin(delegate, anchor, parent); -} - -// static -Menu* Menu::GetSystemMenu(gfx::NativeWindow parent) { - return new views::MenuWin(::GetSystemMenu(parent, FALSE)); -} - -MenuWin::MenuWin(Delegate* d, AnchorPoint anchor, HWND owner) - : Menu(d, anchor), - menu_(CreatePopupMenu()), - owner_(owner), - is_menu_visible_(false), - owner_draw_(l10n_util::NeedOverrideDefaultUIFont(NULL, NULL)) { - DCHECK(delegate()); -} - -MenuWin::MenuWin(HMENU hmenu) - : Menu(NULL, TOPLEFT), - menu_(hmenu), - owner_(NULL), - is_menu_visible_(false), - owner_draw_(false) { - DCHECK(menu_); -} - -MenuWin::~MenuWin() { - STLDeleteContainerPointers(submenus_.begin(), submenus_.end()); - STLDeleteContainerPointers(item_data_.begin(), item_data_.end()); - DestroyMenu(menu_); -} - -void MenuWin::AddMenuItemWithIcon(int index, - int item_id, - const string16& label, - const gfx::ImageSkia& icon) { - owner_draw_ = true; - Menu::AddMenuItemWithIcon(index, item_id, label, icon); -} - -Menu* MenuWin::AddSubMenuWithIcon(int index, - int item_id, - const string16& label, - const gfx::ImageSkia& icon) { - MenuWin* submenu = new MenuWin(this); - submenus_.push_back(submenu); - AddMenuItemInternal(index, item_id, label, icon, submenu->menu_, NORMAL); - return submenu; -} - -void MenuWin::AddSeparator(int index) { - MENUITEMINFO mii; - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_FTYPE; - mii.fType = MFT_SEPARATOR; - InsertMenuItem(menu_, index, TRUE, &mii); -} - -void MenuWin::EnableMenuItemByID(int item_id, bool enabled) { - UINT enable_flags = enabled ? MF_ENABLED : MF_DISABLED | MF_GRAYED; - EnableMenuItem(menu_, item_id, MF_BYCOMMAND | enable_flags); -} - -void MenuWin::EnableMenuItemAt(int index, bool enabled) { - UINT enable_flags = enabled ? MF_ENABLED : MF_DISABLED | MF_GRAYED; - EnableMenuItem(menu_, index, MF_BYPOSITION | enable_flags); -} - -void MenuWin::SetMenuLabel(int item_id, const string16& label) { - MENUITEMINFO mii = {0}; - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_STRING; - mii.dwTypeData = const_cast<wchar_t*>(label.c_str()); - mii.cch = static_cast<UINT>(label.size()); - SetMenuItemInfo(menu_, item_id, false, &mii); -} - -bool MenuWin::SetIcon(const gfx::ImageSkia& icon, int item_id) { - if (!owner_draw_) - owner_draw_ = true; - - const int num_items = GetMenuItemCount(menu_); - int sep_count = 0; - for (int i = 0; i < num_items; ++i) { - if (!(GetMenuState(menu_, i, MF_BYPOSITION) & MF_SEPARATOR)) { - if (ChromeGetMenuItemID(menu_, i) == item_id) { - item_data_[i - sep_count]->icon = icon; - // When the menu is running, we use SetMenuItemInfo to let Windows - // update the item information so that the icon being displayed - // could change immediately. - if (active_host_window) { - MENUITEMINFO mii; - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_FTYPE | MIIM_DATA; - mii.fType = MFT_OWNERDRAW; - mii.dwItemData = - reinterpret_cast<ULONG_PTR>(item_data_[i - sep_count]); - SetMenuItemInfo(menu_, item_id, false, &mii); - } - return true; - } - } else { - ++sep_count; - } - } - - // Continue searching for the item in submenus. - for (size_t i = 0; i < submenus_.size(); ++i) { - if (submenus_[i]->SetIcon(icon, item_id)) - return true; - } - - return false; -} - -void MenuWin::RunMenuAt(int x, int y) { - SetMenuInfo(); - - delegate()->MenuWillShow(); - - // NOTE: we don't use TPM_RIGHTBUTTON here as it breaks selecting by way of - // press, drag, release. See bugs 718 and 8560. - UINT flags = - GetTPMAlignFlags() | TPM_LEFTBUTTON | TPM_RETURNCMD | TPM_RECURSE; - is_menu_visible_ = true; - DCHECK(owner_); - // In order for context menus on menus to work, the context menu needs to - // share the same window as the first menu is parented to. - bool created_host = false; - if (!active_host_window) { - created_host = true; - active_host_window = new MenuHostWindow(this, owner_); - } - UINT selected_id = - TrackPopupMenuEx(menu_, flags, x, y, active_host_window->hwnd(), NULL); - if (created_host) { - delete active_host_window; - active_host_window = NULL; - } - is_menu_visible_ = false; - - // Execute the chosen command - if (selected_id != 0) - delegate()->ExecuteCommand(selected_id); -} - -void MenuWin::Cancel() { - DCHECK(is_menu_visible_); - EndMenu(); -} - -int MenuWin::ItemCount() { - return GetMenuItemCount(menu_); -} - -void MenuWin::AddMenuItemInternal(int index, - int item_id, - const string16& label, - const gfx::ImageSkia& icon, - MenuItemType type) { - AddMenuItemInternal(index, item_id, label, icon, NULL, type); -} - -void MenuWin::AddMenuItemInternal(int index, - int item_id, - const string16& label, - const gfx::ImageSkia& icon, - HMENU submenu, - MenuItemType type) { - DCHECK(type != SEPARATOR) << "Call AddSeparator instead!"; - - if (!owner_draw_ && !icon.isNull()) - owner_draw_ = true; - - if (label.empty() && !delegate()) { - // No label and no delegate; don't add an empty menu. - // It appears under some circumstance we're getting an empty label - // (l10n_util::GetStringUTF16(IDS_TASK_MANAGER) returns ""). This shouldn't - // happen, but I'm working over the crash here. - NOTREACHED(); - return; - } - - MENUITEMINFO mii; - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_FTYPE | MIIM_ID; - if (submenu) { - mii.fMask |= MIIM_SUBMENU; - mii.hSubMenu = submenu; - } - - // Set the type and ID. - if (!owner_draw_) { - mii.fType = MFT_STRING; - mii.fMask |= MIIM_STRING; - } else { - mii.fType = MFT_OWNERDRAW; - } - - if (type == RADIO) - mii.fType |= MFT_RADIOCHECK; - - mii.wID = item_id; - - // Set the item data. - MenuWin::ItemData* data = new ItemData; - item_data_.push_back(data); - data->submenu = submenu != NULL; - - string16 actual_label(label.empty() ? delegate()->GetLabel(item_id) : label); - - // Find out if there is a shortcut we need to append to the label. - ui::Accelerator accelerator(ui::VKEY_UNKNOWN, ui::EF_NONE); - if (delegate() && delegate()->GetAcceleratorInfo(item_id, &accelerator)) { - actual_label += L'\t'; - actual_label += accelerator.GetShortcutText(); - } - labels_.push_back(actual_label); - - if (owner_draw_) { - if (icon.width() != 0 && icon.height() != 0) - data->icon = icon; - else - data->icon = delegate()->GetIcon(item_id); - } else { - mii.dwTypeData = const_cast<wchar_t*>(labels_.back().c_str()); - } - - InsertMenuItem(menu_, index, TRUE, &mii); -} - -MenuWin::MenuWin(MenuWin* parent) - : Menu(parent->delegate(), parent->anchor()), - menu_(CreatePopupMenu()), - owner_(parent->owner_), - is_menu_visible_(false), - owner_draw_(parent->owner_draw_) { -} - -void MenuWin::SetMenuInfo() { - const int num_items = GetMenuItemCount(menu_); - int sep_count = 0; - for (int i = 0; i < num_items; ++i) { - MENUITEMINFO mii_info; - mii_info.cbSize = sizeof(mii_info); - // Get the menu's original type. - mii_info.fMask = MIIM_FTYPE; - GetMenuItemInfo(menu_, i, MF_BYPOSITION, &mii_info); - // Set item states. - if (!(mii_info.fType & MF_SEPARATOR)) { - const int id = ChromeGetMenuItemID(menu_, i); - - MENUITEMINFO mii; - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_STATE | MIIM_FTYPE | MIIM_DATA | MIIM_STRING; - // We also need MFT_STRING for owner drawn items in order to let Windows - // handle the accelerators for us. - mii.fType = MFT_STRING; - if (owner_draw_) - mii.fType |= MFT_OWNERDRAW; - // If the menu originally has radiocheck type, we should follow it. - if (mii_info.fType & MFT_RADIOCHECK) - mii.fType |= MFT_RADIOCHECK; - mii.fState = GetStateFlagsForItemID(id); - - // Validate the label. If there is a contextual label, use it, otherwise - // default to the static label - string16 label; - if (!delegate()->GetContextualLabel(id, &label)) - label = labels_[i - sep_count]; - - if (owner_draw_) { - item_data_[i - sep_count]->label = label; - mii.dwItemData = reinterpret_cast<ULONG_PTR>(item_data_[i - sep_count]); - } - mii.dwTypeData = const_cast<wchar_t*>(label.c_str()); - mii.cch = static_cast<UINT>(label.size()); - SetMenuItemInfo(menu_, i, true, &mii); - } else { - // Set data for owner drawn separators. Set dwItemData NULL to indicate - // a separator. - if (owner_draw_) { - MENUITEMINFO mii; - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_FTYPE; - mii.fType = MFT_SEPARATOR | MFT_OWNERDRAW; - mii.dwItemData = NULL; - SetMenuItemInfo(menu_, i, true, &mii); - } - ++sep_count; - } - } - - for (size_t i = 0; i < submenus_.size(); ++i) - submenus_[i]->SetMenuInfo(); -} - -UINT MenuWin::GetStateFlagsForItemID(int item_id) const { - // Use the delegate to get enabled and checked state. - UINT flags = - delegate()->IsCommandEnabled(item_id) ? MFS_ENABLED : MFS_DISABLED; - - if (delegate()->IsItemChecked(item_id)) - flags |= MFS_CHECKED; - - if (delegate()->IsItemDefault(item_id)) - flags |= MFS_DEFAULT; - - return flags; -} - -DWORD MenuWin::GetTPMAlignFlags() const { - // The manner in which we handle the menu alignment depends on whether or not - // the menu is displayed within a mirrored view. If the UI is mirrored, the - // alignment needs to be fliped so that instead of aligning the menu to the - // right of the point, we align it to the left and vice versa. - DWORD align_flags = TPM_TOPALIGN; - switch (anchor()) { - case TOPLEFT: - if (delegate()->IsRightToLeftUILayout()) { - align_flags |= TPM_RIGHTALIGN; - } else { - align_flags |= TPM_LEFTALIGN; - } - break; - - case TOPRIGHT: - if (delegate()->IsRightToLeftUILayout()) { - align_flags |= TPM_LEFTALIGN; - } else { - align_flags |= TPM_RIGHTALIGN; - } - break; - - default: - NOTREACHED(); - return 0; - } - return align_flags; -} - -} // namespace views diff --git a/chromium/ui/views/controls/menu/menu_win.h b/chromium/ui/views/controls/menu/menu_win.h deleted file mode 100644 index 284f26a4f85..00000000000 --- a/chromium/ui/views/controls/menu/menu_win.h +++ /dev/null @@ -1,139 +0,0 @@ -// 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_CONTROLS_MENU_MENU_WIN_H_ -#define UI_VIEWS_CONTROLS_MENU_MENU_WIN_H_ - -#include <windows.h> - -#include <vector> - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "ui/views/controls/menu/menu.h" - -namespace views { - -namespace { -class MenuHostWindow; -} - -/////////////////////////////////////////////////////////////////////////////// -// -// Menu class -// -// A wrapper around a Win32 HMENU handle that provides convenient APIs for -// menu construction, display and subsequent command execution. -// -/////////////////////////////////////////////////////////////////////////////// -class MenuWin : public Menu { - public: - // Construct a Menu using the specified controller to determine command - // state. - // delegate A Menu::Delegate implementation that provides more - // information about the Menu presentation. - // anchor An alignment hint for the popup menu. - // owner The window that the menu is being brought up relative - // to. Not actually used for anything but must not be - // NULL. - MenuWin(Delegate* d, AnchorPoint anchor, HWND owner); - // Alternatively, a Menu object can be constructed wrapping an existing - // HMENU. This can be used to use the convenience methods to insert - // menu items and manage label string ownership. However this kind of - // Menu object cannot use the delegate. - explicit MenuWin(HMENU hmenu); - virtual ~MenuWin(); - - // Overridden from Menu: - virtual void AddMenuItemWithIcon(int index, - int item_id, - const string16& label, - const gfx::ImageSkia& icon) OVERRIDE; - virtual Menu* AddSubMenuWithIcon(int index, - int item_id, - const string16& label, - const gfx::ImageSkia& icon) OVERRIDE; - virtual void AddSeparator(int index) OVERRIDE; - virtual void EnableMenuItemByID(int item_id, bool enabled) OVERRIDE; - virtual void EnableMenuItemAt(int index, bool enabled) OVERRIDE; - virtual void SetMenuLabel(int item_id, const string16& label) OVERRIDE; - virtual bool SetIcon(const gfx::ImageSkia& icon, int item_id) OVERRIDE; - virtual void RunMenuAt(int x, int y) OVERRIDE; - virtual void Cancel() OVERRIDE; - virtual int ItemCount() OVERRIDE; - - virtual HMENU GetMenuHandle() const { - return menu_; - } - - // Gets the Win32 TPM alignment flags for the specified AnchorPoint. - DWORD GetTPMAlignFlags() const; - - protected: - virtual void AddMenuItemInternal(int index, - int item_id, - const string16& label, - const gfx::ImageSkia& icon, - MenuItemType type) OVERRIDE; - - private: - friend class MenuHostWindow; - - // The data of menu items needed to display. - struct ItemData; - - void AddMenuItemInternal(int index, - int item_id, - const string16& label, - const gfx::ImageSkia& icon, - HMENU submenu, - MenuItemType type); - - explicit MenuWin(MenuWin* parent); - - // Sets menu information before displaying, including sub-menus. - void SetMenuInfo(); - - // Get all the state flags for the |fState| field of MENUITEMINFO for the - // item with the specified id. |delegate| is consulted if non-NULL about - // the state of the item in preference to |controller_|. - UINT GetStateFlagsForItemID(int item_id) const; - - // The Win32 Menu Handle we wrap - HMENU menu_; - - // The window that would receive WM_COMMAND messages when the user selects - // an item from the menu. - HWND owner_; - - // This list is used to store the default labels for the menu items. - // We may use contextual labels when RunMenu is called, so we must save - // a copy of default ones here. - std::vector<string16> labels_; - - // A flag to indicate whether this menu will be drawn by the Menu class. - // If it's true, all the menu items will be owner drawn. Otherwise, - // all the drawing will be done by Windows. - bool owner_draw_; - - // This list is to store the string labels and icons to display. It's used - // when owner_draw_ is true. We give MENUITEMINFO pointers to these - // structures to specify what we'd like to draw. If owner_draw_ is false, - // we only give MENUITEMINFO pointers to the labels_. - // The label member of the ItemData structure comes from either labels_ or - // the GetContextualLabel. - std::vector<ItemData*> item_data_; - - // Our sub-menus, if any. - std::vector<MenuWin*> submenus_; - - // Whether the menu is visible. - bool is_menu_visible_; - - DISALLOW_COPY_AND_ASSIGN(MenuWin); -}; - -} // namespace views - -#endif // UI_VIEWS_CONTROLS_MENU_MENU_WIN_H_ diff --git a/chromium/ui/views/controls/menu/native_menu_win.cc b/chromium/ui/views/controls/menu/native_menu_win.cc index c260af9bcce..caddd70c4a5 100644 --- a/chromium/ui/views/controls/menu/native_menu_win.cc +++ b/chromium/ui/views/controls/menu/native_menu_win.cc @@ -18,10 +18,11 @@ #include "ui/base/models/menu_model.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/canvas.h" -#include "ui/gfx/font.h" +#include "ui/gfx/font_list.h" +#include "ui/gfx/geometry/rect.h" #include "ui/gfx/image/image.h" #include "ui/gfx/image/image_skia.h" -#include "ui/gfx/rect.h" +#include "ui/gfx/text_utils.h" #include "ui/gfx/win/hwnd_util.h" #include "ui/native_theme/native_theme.h" #include "ui/native_theme/native_theme_win.h" @@ -51,7 +52,7 @@ struct NativeMenuWin::ItemData { // The Windows API requires that whoever creates the menus must own the // strings used for labels, and keep them around for the lifetime of the // created menu. So be it. - string16 label; + base::string16 label; // Someone needs to own submenus, it may as well be us. scoped_ptr<Menu2> submenu; @@ -171,17 +172,18 @@ class NativeMenuWin::MenuHostWindow { void OnMeasureItem(WPARAM w_param, MEASUREITEMSTRUCT* measure_item_struct) { NativeMenuWin::ItemData* data = GetItemData(measure_item_struct->itemData); if (data) { - gfx::Font font; - measure_item_struct->itemWidth = font.GetStringWidth(data->label) + + gfx::FontList font_list; + measure_item_struct->itemWidth = + gfx::GetStringWidth(data->label, font_list) + kIconWidth + kItemLeftMargin + views::kItemLabelSpacing - GetSystemMetrics(SM_CXMENUCHECK); if (data->submenu.get()) measure_item_struct->itemWidth += kArrowWidth; // If the label contains an accelerator, make room for tab. - if (data->label.find(L'\t') != string16::npos) - measure_item_struct->itemWidth += font.GetStringWidth(L" "); + if (data->label.find(L'\t') != base::string16::npos) + measure_item_struct->itemWidth += gfx::GetStringWidth(L" ", font_list); measure_item_struct->itemHeight = - font.GetHeight() + kItemBottomMargin + kItemTopMargin; + font_list.GetHeight() + kItemBottomMargin + kItemTopMargin; } else { // Measure separator size. measure_item_struct->itemHeight = GetSystemMetrics(SM_CYMENU) / 2; @@ -225,19 +227,19 @@ class NativeMenuWin::MenuHostWindow { SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &underline_mnemonics, 0); if (!underline_mnemonics) format |= DT_HIDEPREFIX; - gfx::Font font; - HGDIOBJ old_font = - static_cast<HFONT>(SelectObject(dc, font.GetNativeFont())); + gfx::FontList font_list; + HGDIOBJ old_font = static_cast<HFONT>( + SelectObject(dc, font_list.GetPrimaryFont().GetNativeFont())); // If an accelerator is specified (with a tab delimiting the rest of the // label from the accelerator), we have to justify the fist part on the // left and the accelerator on the right. // TODO(jungshik): This will break in RTL UI. Currently, he/ar use the // window system UI font and will not hit here. - string16 label = data->label; - string16 accel; - string16::size_type tab_pos = label.find(L'\t'); - if (tab_pos != string16::npos) { + base::string16 label = data->label; + base::string16 accel; + base::string16::size_type tab_pos = label.find(L'\t'); + if (tab_pos != base::string16::npos) { accel = label.substr(tab_pos); label = label.substr(0, tab_pos); } @@ -626,7 +628,7 @@ void NativeMenuWin::AddMenuItemAt(int menu_index, int model_index) { mii.fType = MFT_OWNERDRAW; ItemData* item_data = new ItemData; - item_data->label = string16(); + item_data->label = base::string16(); ui::MenuModel::ItemType type = model_->GetTypeAt(model_index); if (type == ui::MenuModel::TYPE_SUBMENU) { item_data->submenu.reset(new Menu2(model_->GetSubmenuModelAt(model_index))); @@ -678,7 +680,7 @@ void NativeMenuWin::SetMenuItemState(int menu_index, bool enabled, bool checked, void NativeMenuWin::SetMenuItemLabel(int menu_index, int model_index, - const string16& label) { + const base::string16& label) { if (IsSeparatorItemAt(menu_index)) return; @@ -690,8 +692,8 @@ void NativeMenuWin::SetMenuItemLabel(int menu_index, void NativeMenuWin::UpdateMenuItemInfoForString(MENUITEMINFO* mii, int model_index, - const string16& label) { - string16 formatted = label; + const base::string16& label) { + base::string16 formatted = label; ui::MenuModel::ItemType type = model_->GetTypeAt(model_index); // Strip out any tabs, otherwise they get interpreted as accelerators and can // lead to weird behavior. diff --git a/chromium/ui/views/controls/menu/native_menu_win.h b/chromium/ui/views/controls/menu/native_menu_win.h index ca6f37bc5c8..ce3a072e416 100644 --- a/chromium/ui/views/controls/menu/native_menu_win.h +++ b/chromium/ui/views/controls/menu/native_menu_win.h @@ -74,7 +74,7 @@ class VIEWS_EXPORT NativeMenuWin : public MenuWrapper { // Sets the label of the item at the specified index. void SetMenuItemLabel(int menu_index, int model_index, - const string16& label); + const base::string16& label); // Updates the local data structure with the correctly formatted version of // |label| at the specified model_index, and adds string data to |mii| if @@ -82,7 +82,7 @@ class VIEWS_EXPORT NativeMenuWin : public MenuWrapper { // of the peculiarities of the Windows menu API. void UpdateMenuItemInfoForString(MENUITEMINFO* mii, int model_index, - const string16& label); + const base::string16& label); // Returns the alignment flags to be passed to TrackPopupMenuEx, based on the // supplied alignment and the UI text direction. diff --git a/chromium/ui/views/controls/menu/submenu_view.cc b/chromium/ui/views/controls/menu/submenu_view.cc index 9184ca9abfb..a6ef56a888e 100644 --- a/chromium/ui/views/controls/menu/submenu_view.cc +++ b/chromium/ui/views/controls/menu/submenu_view.cc @@ -7,12 +7,14 @@ #include <algorithm> #include "base/compiler_specific.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/events/event.h" #include "ui/gfx/canvas.h" +#include "ui/gfx/geometry/safe_integer_conversions.h" #include "ui/views/controls/menu/menu_config.h" #include "ui/views/controls/menu/menu_controller.h" #include "ui/views/controls/menu/menu_host.h" +#include "ui/views/controls/menu/menu_item_view.h" #include "ui/views/controls/menu/menu_scroll_view_container.h" #include "ui/views/widget/root_view.h" #include "ui/views/widget/widget.h" @@ -41,7 +43,9 @@ SubmenuView::SubmenuView(MenuItemView* parent) max_minor_text_width_(0), minimum_preferred_width_(0), resize_open_menu_(false), - scroll_animator_(new ScrollAnimator(this)) { + scroll_animator_(new ScrollAnimator(this)), + roundoff_error_(0), + prefix_selector_(this) { DCHECK(parent); // We'll delete ourselves, otherwise the ScrollView would delete us on close. set_owned_by_client(); @@ -117,7 +121,7 @@ void SubmenuView::Layout() { } } -gfx::Size SubmenuView::GetPreferredSize() { +gfx::Size SubmenuView::GetPreferredSize() const { if (!has_children()) return gfx::Size(); @@ -128,11 +132,11 @@ gfx::Size SubmenuView::GetPreferredSize() { int max_simple_width = 0; int height = 0; for (int i = 0; i < child_count(); ++i) { - View* child = child_at(i); + const View* child = child_at(i); if (!child->visible()) continue; if (child->id() == MenuItemView::kMenuItemViewID) { - MenuItemView* menu = static_cast<MenuItemView*>(child); + const MenuItemView* menu = static_cast<const MenuItemView*>(child); const MenuItemView::MenuItemDimensions& dimensions = menu->GetDimensions(); max_simple_width = std::max( @@ -162,15 +166,20 @@ gfx::Size SubmenuView::GetPreferredSize() { height + insets.height()); } -void SubmenuView::GetAccessibleState(ui::AccessibleViewState* state) { +void SubmenuView::GetAccessibleState(ui::AXViewState* state) { // Inherit most of the state from the parent menu item, except the role. if (GetMenuItem()) GetMenuItem()->GetAccessibleState(state); - state->role = ui::AccessibilityTypes::ROLE_MENUPOPUP; + state->role = ui::AX_ROLE_MENU_LIST_POPUP; +} + +ui::TextInputClient* SubmenuView::GetTextInputClient() { + return &prefix_selector_; } -void SubmenuView::PaintChildren(gfx::Canvas* canvas) { - View::PaintChildren(canvas); +void SubmenuView::PaintChildren(gfx::Canvas* canvas, + const views::CullSet& cull_set) { + View::PaintChildren(canvas, cull_set); if (drop_item_ && drop_position_ != MenuDelegate::DROP_ON) PaintDropIndicator(canvas, drop_item_, drop_position_); @@ -293,6 +302,35 @@ void SubmenuView::OnGestureEvent(ui::GestureEvent* event) { event->SetHandled(); } +int SubmenuView::GetRowCount() { + return GetMenuItemCount(); +} + +int SubmenuView::GetSelectedRow() { + int row = 0; + for (int i = 0; i < child_count(); ++i) { + if (child_at(i)->id() != MenuItemView::kMenuItemViewID) + continue; + + if (static_cast<MenuItemView*>(child_at(i))->IsSelected()) + return row; + + row++; + } + + return -1; +} + +void SubmenuView::SetSelectedRow(int row) { + GetMenuItem()->GetMenuController()->SetSelection( + GetMenuItemAt(row), + MenuController::SELECTION_DEFAULT); +} + +base::string16 SubmenuView::GetTextForRow(int row) { + return GetMenuItemAt(row)->title(); +} + bool SubmenuView::IsShowing() { return host_ && host_->IsMenuHostVisible(); } @@ -313,10 +351,10 @@ void SubmenuView::ShowAt(Widget* parent, } GetScrollViewContainer()->NotifyAccessibilityEvent( - ui::AccessibilityTypes::EVENT_MENUSTART, + ui::AX_EVENT_MENU_START, true); NotifyAccessibilityEvent( - ui::AccessibilityTypes::EVENT_MENUPOPUPSTART, + ui::AX_EVENT_MENU_POPUP_START, true); } @@ -327,9 +365,9 @@ void SubmenuView::Reposition(const gfx::Rect& bounds) { void SubmenuView::Close() { if (host_) { - NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_MENUPOPUPEND, true); + NotifyAccessibilityEvent(ui::AX_EVENT_MENU_POPUP_END, true); GetScrollViewContainer()->NotifyAccessibilityEvent( - ui::AccessibilityTypes::EVENT_MENUEND, true); + ui::AX_EVENT_MENU_END, true); host_->DestroyMenuHost(); host_ = NULL; @@ -446,7 +484,9 @@ bool SubmenuView::OnScroll(float dx, float dy) { const gfx::Rect& vis_bounds = GetVisibleBounds(); const gfx::Rect& full_bounds = bounds(); int x = vis_bounds.x(); - int y = vis_bounds.y() - static_cast<int>(dy); + float y_f = vis_bounds.y() - dy - roundoff_error_; + int y = gfx::ToRoundedInt(y_f); + roundoff_error_ = y - y_f; // clamp y to [0, full_height - vis_height) y = std::min(y, full_bounds.height() - vis_bounds.height() - 1); y = std::max(y, 0); diff --git a/chromium/ui/views/controls/menu/submenu_view.h b/chromium/ui/views/controls/menu/submenu_view.h index 19e0d46ffa2..1b7d36243d2 100644 --- a/chromium/ui/views/controls/menu/submenu_view.h +++ b/chromium/ui/views/controls/menu/submenu_view.h @@ -10,6 +10,8 @@ #include "base/compiler_specific.h" #include "ui/views/animation/scroll_animator.h" #include "ui/views/controls/menu/menu_delegate.h" +#include "ui/views/controls/prefix_delegate.h" +#include "ui/views/controls/prefix_selector.h" #include "ui/views/view.h" namespace views { @@ -26,14 +28,14 @@ class MenuScrollViewContainer; // . Forwards the appropriate events to the MenuController. This allows the // MenuController to update the selection as the user moves the mouse around. // . Renders the drop indicator during a drop operation. -// . Shows and hides the window (a NativeWidgetWin) when the menu is shown on +// . Shows and hides the window (a NativeWidget) when the menu is shown on // screen. // // SubmenuView is itself contained in a MenuScrollViewContainer. // MenuScrollViewContainer handles showing as much of the SubmenuView as the // screen allows. If the SubmenuView is taller than the screen, scroll buttons // are provided that allow the user to see all the menu items. -class VIEWS_EXPORT SubmenuView : public View, +class VIEWS_EXPORT SubmenuView : public PrefixDelegate, public ScrollDelegate { public: // The submenu's class name. @@ -53,13 +55,15 @@ class VIEWS_EXPORT SubmenuView : public View, // Positions and sizes the child views. This tiles the views vertically, // giving each child the available width. virtual void Layout() OVERRIDE; - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; // Override from View. - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; + virtual ui::TextInputClient* GetTextInputClient() OVERRIDE; // Painting. - virtual void PaintChildren(gfx::Canvas* canvas) OVERRIDE; + virtual void PaintChildren(gfx::Canvas* canvas, + const views::CullSet& cull_view) OVERRIDE; // Drag and drop methods. These are forwarded to the MenuController. virtual bool GetDropFormats( @@ -79,6 +83,12 @@ class VIEWS_EXPORT SubmenuView : public View, // Scrolls on menu item boundaries. virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; + // Overridden from PrefixDelegate. + virtual int GetRowCount() OVERRIDE; + virtual int GetSelectedRow() OVERRIDE; + virtual void SetSelectedRow(int row) OVERRIDE; + virtual base::string16 GetTextForRow(int row) OVERRIDE; + // Returns true if the menu is showing. bool IsShowing(); @@ -191,7 +201,7 @@ class VIEWS_EXPORT SubmenuView : public View, MenuScrollViewContainer* scroll_view_container_; // See description above getter. - int max_minor_text_width_; + mutable int max_minor_text_width_; // Minimum width returned in GetPreferredSize(). int minimum_preferred_width_; @@ -202,6 +212,14 @@ class VIEWS_EXPORT SubmenuView : public View, // The submenu's scroll animator scoped_ptr<ScrollAnimator> scroll_animator_; + // Difference between current position and cumulative deltas passed to + // OnScroll. + // TODO(tdresser): This should be removed when raw pixel scrolling for views + // is enabled. See crbug.com/329354. + float roundoff_error_; + + PrefixSelector prefix_selector_; + DISALLOW_COPY_AND_ASSIGN(SubmenuView); }; diff --git a/chromium/ui/views/controls/message_box_view.cc b/chromium/ui/views/controls/message_box_view.cc index 3c2cde95292..671ca6194ec 100644 --- a/chromium/ui/views/controls/message_box_view.cc +++ b/chromium/ui/views/controls/message_box_view.cc @@ -8,7 +8,7 @@ #include "base/message_loop/message_loop.h" #include "base/strings/string_split.h" #include "base/strings/utf_string_conversions.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/base/clipboard/clipboard.h" #include "ui/base/clipboard/scoped_clipboard_writer.h" #include "ui/views/controls/button/checkbox.h" @@ -36,7 +36,7 @@ const int kDefaultMessageWidth = 320; // 001C..001E ; B # Cc [3] <control-001C>..<control-001E> // 0085 ; B # Cc <control-0085> // 2029 ; B # Zp PARAGRAPH SEPARATOR -bool IsParagraphSeparator(char16 c) { +bool IsParagraphSeparator(base::char16 c) { return ( c == 0x000A || c == 0x000D || c == 0x001C || c == 0x001D || c == 0x001E || c == 0x0085 || c == 0x2029); } @@ -44,8 +44,8 @@ bool IsParagraphSeparator(char16 c) { // Splits |text| into a vector of paragraphs. // Given an example "\nabc\ndef\n\n\nhij\n", the split results should be: // "", "abc", "def", "", "", "hij", and "". -void SplitStringIntoParagraphs(const string16& text, - std::vector<string16>* paragraphs) { +void SplitStringIntoParagraphs(const base::string16& text, + std::vector<base::string16>* paragraphs) { paragraphs->clear(); size_t start = 0; @@ -65,7 +65,7 @@ namespace views { /////////////////////////////////////////////////////////////////////////////// // MessageBoxView, public: -MessageBoxView::InitParams::InitParams(const string16& message) +MessageBoxView::InitParams::InitParams(const base::string16& message) : options(NO_OPTIONS), message(message), message_width(kDefaultMessageWidth), @@ -85,8 +85,8 @@ MessageBoxView::MessageBoxView(const InitParams& params) MessageBoxView::~MessageBoxView() {} -string16 MessageBoxView::GetInputText() { - return prompt_field_ ? prompt_field_->text() : string16(); +base::string16 MessageBoxView::GetInputText() { + return prompt_field_ ? prompt_field_->text() : base::string16(); } bool MessageBoxView::IsCheckBoxSelected() { @@ -101,7 +101,7 @@ void MessageBoxView::SetIcon(const gfx::ImageSkia& icon) { ResetLayoutManager(); } -void MessageBoxView::SetCheckBoxLabel(const string16& label) { +void MessageBoxView::SetCheckBoxLabel(const base::string16& label) { if (!checkbox_) checkbox_ = new Checkbox(label); else @@ -115,7 +115,8 @@ void MessageBoxView::SetCheckBoxSelected(bool selected) { checkbox_->SetChecked(selected); } -void MessageBoxView::SetLink(const string16& text, LinkListener* listener) { +void MessageBoxView::SetLink(const base::string16& text, + LinkListener* listener) { if (text.empty()) { DCHECK(!listener); delete link_; @@ -132,8 +133,8 @@ void MessageBoxView::SetLink(const string16& text, LinkListener* listener) { ResetLayoutManager(); } -void MessageBoxView::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_ALERT; +void MessageBoxView::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_ALERT; } /////////////////////////////////////////////////////////////////////////////// @@ -145,7 +146,7 @@ void MessageBoxView::ViewHierarchyChanged( if (prompt_field_) prompt_field_->SelectAll(true); - NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_ALERT, true); + NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true); } } @@ -162,7 +163,7 @@ bool MessageBoxView::AcceleratorPressed(const ui::Accelerator& accelerator) { return false; ui::ScopedClipboardWriter scw(clipboard, ui::CLIPBOARD_TYPE_COPY_PASTE); - string16 text = message_labels_[0]->text(); + base::string16 text = message_labels_[0]->text(); for (size_t i = 1; i < message_labels_.size(); ++i) text += message_labels_[i]->text(); scw.WriteText(text); @@ -174,23 +175,15 @@ bool MessageBoxView::AcceleratorPressed(const ui::Accelerator& accelerator) { void MessageBoxView::Init(const InitParams& params) { if (params.options & DETECT_DIRECTIONALITY) { - std::vector<string16> texts; + std::vector<base::string16> texts; SplitStringIntoParagraphs(params.message, &texts); - // If the text originates from a web page, its alignment is based on its - // first character with strong directionality. - base::i18n::TextDirection message_direction = - base::i18n::GetFirstStrongCharacterDirection(params.message); - gfx::HorizontalAlignment alignment = - (message_direction == base::i18n::RIGHT_TO_LEFT) ? - gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT; for (size_t i = 0; i < texts.size(); ++i) { Label* message_label = new Label(texts[i]); - // Don't set multi-line to true if the text is empty, else the label will - // have a height of 0. + // Avoid empty multi-line labels, which have a height of 0. message_label->SetMultiLine(!texts[i].empty()); message_label->SetAllowCharacterBreak(true); - message_label->set_directionality_mode(Label::AUTO_DETECT_DIRECTIONALITY); - message_label->SetHorizontalAlignment(alignment); + message_label->set_directionality_mode(gfx::DIRECTIONALITY_FROM_TEXT); + message_label->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD); message_labels_.push_back(message_label); } } else { diff --git a/chromium/ui/views/controls/message_box_view.h b/chromium/ui/views/controls/message_box_view.h index 1d92a379be4..cb708e6013d 100644 --- a/chromium/ui/views/controls/message_box_view.h +++ b/chromium/ui/views/controls/message_box_view.h @@ -42,12 +42,12 @@ class VIEWS_EXPORT MessageBoxView : public View { }; struct VIEWS_EXPORT InitParams { - explicit InitParams(const string16& message); + explicit InitParams(const base::string16& message); ~InitParams(); uint16 options; - string16 message; - string16 default_prompt; + base::string16 message; + base::string16 default_prompt; int message_width; int inter_row_vertical_spacing; }; @@ -60,7 +60,7 @@ class VIEWS_EXPORT MessageBoxView : public View { views::Textfield* text_box() { return prompt_field_; } // Returns user entered data in the prompt field. - string16 GetInputText(); + base::string16 GetInputText(); // Returns true if a checkbox is selected, false otherwise. (And false if // the message box has no checkbox.) @@ -73,17 +73,17 @@ class VIEWS_EXPORT MessageBoxView : public View { // Adds a checkbox with the specified label to the message box if this is the // first call. Otherwise, it changes the label of the current checkbox. To // start, the message box has no checkbox until this function is called. - void SetCheckBoxLabel(const string16& label); + void SetCheckBoxLabel(const base::string16& label); // Sets the state of the check-box. void SetCheckBoxSelected(bool selected); // Sets the text and the listener of the link. If |text| is empty, the link // is removed. - void SetLink(const string16& text, LinkListener* listener); + void SetLink(const base::string16& text, LinkListener* listener); // View: - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; protected: // View: diff --git a/chromium/ui/views/controls/native/native_view_host.cc b/chromium/ui/views/controls/native/native_view_host.cc index 28b1fe3aec7..c1b230c452e 100644 --- a/chromium/ui/views/controls/native/native_view_host.cc +++ b/chromium/ui/views/controls/native/native_view_host.cc @@ -5,6 +5,7 @@ #include "ui/views/controls/native/native_view_host.h" #include "base/logging.h" +#include "ui/base/cursor/cursor.h" #include "ui/gfx/canvas.h" #include "ui/views/accessibility/native_view_accessibility.h" #include "ui/views/controls/native/native_view_host_wrapper.h" @@ -16,16 +17,6 @@ namespace views { const char NativeViewHost::kViewClassName[] = "NativeViewHost"; const char kWidgetNativeViewHostKey[] = "WidgetNativeViewHost"; -#if defined(USE_AURA) -// Views implementation draws the focus. -// TODO(oshima): Eliminate this flag and consolidate -// the focus border code. -const bool NativeViewHost::kRenderNativeControlFocus = false; -#else -// static -const bool NativeViewHost::kRenderNativeControlFocus = true; -#endif - //////////////////////////////////////////////////////////////////////////////// // NativeViewHost, public: @@ -74,7 +65,7 @@ void NativeViewHost::NativeViewDestroyed() { //////////////////////////////////////////////////////////////////////////////// // NativeViewHost, View overrides: -gfx::Size NativeViewHost::GetPreferredSize() { +gfx::Size NativeViewHost::GetPreferredSize() const { return preferred_size_; } @@ -182,7 +173,7 @@ const char* NativeViewHost::GetClassName() const { void NativeViewHost::OnFocus() { native_wrapper_->SetFocus(); - NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_FOCUS, true); + NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true); } gfx::NativeViewAccessible NativeViewHost::GetNativeViewAccessible() { @@ -196,6 +187,10 @@ gfx::NativeViewAccessible NativeViewHost::GetNativeViewAccessible() { return View::GetNativeViewAccessible(); } +gfx::NativeCursor NativeViewHost::GetCursor(const ui::MouseEvent& event) { + return native_wrapper_->GetCursor(event.x(), event.y()); +} + //////////////////////////////////////////////////////////////////////////////// // NativeViewHost, private: @@ -226,5 +221,4 @@ void NativeViewHost::ClearFocus() { } } - } // namespace views diff --git a/chromium/ui/views/controls/native/native_view_host.h b/chromium/ui/views/controls/native/native_view_host.h index bdc51510550..d5dd9b59fdb 100644 --- a/chromium/ui/views/controls/native/native_view_host.h +++ b/chromium/ui/views/controls/native/native_view_host.h @@ -28,9 +28,6 @@ class VIEWS_EXPORT NativeViewHost : public View { // The NativeViewHost's class name. static const char kViewClassName[]; - // Should views render the focus when on native controls? - static const bool kRenderNativeControlFocus; - NativeViewHost(); virtual ~NativeViewHost(); @@ -80,12 +77,13 @@ class VIEWS_EXPORT NativeViewHost : public View { void NativeViewDestroyed(); // Overridden from View: - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual void Layout() OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; virtual void VisibilityChanged(View* starting_from, bool is_visible) OVERRIDE; virtual void OnFocus() OVERRIDE; virtual gfx::NativeViewAccessible GetNativeViewAccessible() OVERRIDE; + virtual gfx::NativeCursor GetCursor(const ui::MouseEvent& event) OVERRIDE; protected: virtual bool NeedsNotificationWhenVisibleBoundsChange() const OVERRIDE; diff --git a/chromium/ui/views/controls/native/native_view_host_aura.cc b/chromium/ui/views/controls/native/native_view_host_aura.cc index a71e230be47..49bf733bbe2 100644 --- a/chromium/ui/views/controls/native/native_view_host_aura.cc +++ b/chromium/ui/views/controls/native/native_view_host_aura.cc @@ -7,6 +7,7 @@ #include "base/logging.h" #include "ui/aura/client/focus_client.h" #include "ui/aura/window.h" +#include "ui/base/cursor/cursor.h" #include "ui/views/controls/native/native_view_host.h" #include "ui/views/view_constants_aura.h" #include "ui/views/widget/widget.h" @@ -100,6 +101,12 @@ gfx::NativeViewAccessible NativeViewHostAura::GetNativeViewAccessible() { return NULL; } +gfx::NativeCursor NativeViewHostAura::GetCursor(int x, int y) { + if (host_->native_view()) + return host_->native_view()->GetCursor(gfx::Point(x, y)); + return gfx::kNullCursor; +} + void NativeViewHostAura::OnWindowDestroyed(aura::Window* window) { DCHECK(window == host_->native_view()); host_->NativeViewDestroyed(); diff --git a/chromium/ui/views/controls/native/native_view_host_aura.h b/chromium/ui/views/controls/native/native_view_host_aura.h index eba4b99c9fd..8ef4e455f3d 100644 --- a/chromium/ui/views/controls/native/native_view_host_aura.h +++ b/chromium/ui/views/controls/native/native_view_host_aura.h @@ -34,6 +34,7 @@ class VIEWS_EXPORT NativeViewHostAura : public NativeViewHostWrapper, virtual void HideWidget() OVERRIDE; virtual void SetFocus() OVERRIDE; virtual gfx::NativeViewAccessible GetNativeViewAccessible() OVERRIDE; + virtual gfx::NativeCursor GetCursor(int x, int y) OVERRIDE; private: // Overridden from aura::WindowObserver: diff --git a/chromium/ui/views/controls/native/native_view_host_aura_unittest.cc b/chromium/ui/views/controls/native/native_view_host_aura_unittest.cc index 7653cc905c3..f6dc561f2f0 100644 --- a/chromium/ui/views/controls/native/native_view_host_aura_unittest.cc +++ b/chromium/ui/views/controls/native/native_view_host_aura_unittest.cc @@ -7,6 +7,7 @@ #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" #include "ui/aura/window.h" +#include "ui/base/cursor/cursor.h" #include "ui/views/controls/native/native_view_host.h" #include "ui/views/test/views_test_base.h" #include "ui/views/view.h" @@ -24,6 +25,10 @@ class NativeViewHostAuraTest : public ViewsTestBase { return static_cast<NativeViewHostAura*>(host_->native_wrapper_.get()); } + Widget* toplevel() { + return toplevel_.get(); + } + NativeViewHost* host() { return host_.get(); } @@ -95,4 +100,18 @@ TEST_F(NativeViewHostAuraTest, HostViewPropertyKey) { EXPECT_FALSE(child_win->GetProperty(views::kHostViewKey)); } +// Tests that the NativeViewHost reports the cursor set on its native view. +TEST_F(NativeViewHostAuraTest, CursorForNativeView) { + CreateHost(); + + toplevel()->SetCursor(ui::kCursorHand); + child()->SetCursor(ui::kCursorWait); + ui::MouseEvent move_event(ui::ET_MOUSE_MOVED, gfx::Point(0, 0), + gfx::Point(0, 0), 0, 0); + + EXPECT_EQ(ui::kCursorWait, host()->GetCursor(move_event).native_type()); + + DestroyHost(); +} + } // namespace views diff --git a/chromium/ui/views/controls/native/native_view_host_mac.cc b/chromium/ui/views/controls/native/native_view_host_mac.cc new file mode 100644 index 00000000000..53bb00b7a0e --- /dev/null +++ b/chromium/ui/views/controls/native/native_view_host_mac.cc @@ -0,0 +1,18 @@ +// Copyright 2014 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/controls/native/native_view_host_wrapper.h" + +namespace views { + +//////////////////////////////////////////////////////////////////////////////// +// NativeViewHostWrapper, public: + +// static +NativeViewHostWrapper* NativeViewHostWrapper::CreateWrapper( + NativeViewHost* host) { + return NULL; +} + +} // namespace views diff --git a/chromium/ui/views/controls/native/native_view_host_unittest.cc b/chromium/ui/views/controls/native/native_view_host_unittest.cc index f30980bca8b..247782437a5 100644 --- a/chromium/ui/views/controls/native/native_view_host_unittest.cc +++ b/chromium/ui/views/controls/native/native_view_host_unittest.cc @@ -4,19 +4,12 @@ #include "ui/views/controls/native/native_view_host.h" -#if defined(OS_WIN) && !defined(USE_AURA) -#include <windows.h> -#endif - #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" +#include "ui/aura/window.h" #include "ui/views/test/views_test_base.h" #include "ui/views/widget/widget.h" -#if defined(USE_AURA) -#include "ui/aura/window.h" -#endif - namespace views { class NativeViewHostTest : public ViewsTestBase { @@ -93,15 +86,9 @@ class NativeViewHierarchyChangedTestView : public View { DISALLOW_COPY_AND_ASSIGN(NativeViewHierarchyChangedTestView); }; -#if defined(USE_AURA) aura::Window* GetNativeParent(aura::Window* window) { return window->parent(); } -#elif defined(OS_WIN) -HWND GetNativeParent(HWND window) { - return GetParent(window); -} -#endif class ViewHierarchyChangedTestHost : public NativeViewHost { public: diff --git a/chromium/ui/views/controls/native/native_view_host_win.cc b/chromium/ui/views/controls/native/native_view_host_win.cc deleted file mode 100644 index e7b74c3c7a1..00000000000 --- a/chromium/ui/views/controls/native/native_view_host_win.cc +++ /dev/null @@ -1,157 +0,0 @@ -// 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/controls/native/native_view_host_win.h" - -#include <oleacc.h> - -#include "base/logging.h" -#include "ui/base/win/hidden_window.h" -#include "ui/gfx/canvas.h" -#include "ui/gfx/win/dpi.h" -#include "ui/gfx/win/window_impl.h" -#include "ui/views/controls/native/native_view_host.h" -#include "ui/views/focus/focus_manager.h" -#include "ui/views/widget/native_widget.h" -#include "ui/views/widget/root_view.h" -#include "ui/views/widget/widget.h" - -namespace views { - -//////////////////////////////////////////////////////////////////////////////// -// NativeViewHostWin, public: - -NativeViewHostWin::NativeViewHostWin(NativeViewHost* host) - : host_(host), - installed_clip_(false) { -} - -NativeViewHostWin::~NativeViewHostWin() { -} - -//////////////////////////////////////////////////////////////////////////////// -// NativeViewHostWin, NativeViewHostWrapper implementation: -void NativeViewHostWin::NativeViewWillAttach() { - // First hide the new window. We don't want anything to draw (like sub-hwnd - // borders), when NativeViewHost changes the parent. - ShowWindow(host_->native_view(), SW_HIDE); -} - -void NativeViewHostWin::NativeViewDetaching(bool destroyed) { - if (!destroyed) { - if (installed_clip_) - UninstallClip(); - Widget::ReparentNativeView(host_->native_view(), ui::GetHiddenWindow()); - } - installed_clip_ = false; -} - -void NativeViewHostWin::AddedToWidget() { - if (!IsWindow(host_->native_view())) - return; - HWND parent_hwnd = GetParent(host_->native_view()); - HWND widget_hwnd = host_->GetWidget()->GetNativeView(); - if (parent_hwnd != widget_hwnd) - SetParent(host_->native_view(), widget_hwnd); - if (host_->IsDrawn()) - ShowWindow(host_->native_view(), SW_SHOW); - else - ShowWindow(host_->native_view(), SW_HIDE); - host_->Layout(); -} - -void NativeViewHostWin::RemovedFromWidget() { - if (!IsWindow(host_->native_view())) - return; - ShowWindow(host_->native_view(), SW_HIDE); - SetParent(host_->native_view(), NULL); -} - -void NativeViewHostWin::InstallClip(int x, int y, int w, int h) { - HRGN clip_region = CreateRectRgn(x, y, x + w, y + h); - // NOTE: SetWindowRgn owns the region (as well as the deleting the - // current region), as such we don't delete the old region. - SetWindowRgn(host_->native_view(), clip_region, TRUE); - installed_clip_ = true; -} - -bool NativeViewHostWin::HasInstalledClip() { - return installed_clip_; -} - -void NativeViewHostWin::UninstallClip() { - SetWindowRgn(host_->native_view(), 0, TRUE); - installed_clip_ = false; -} - -void NativeViewHostWin::ShowWidget(int x, int y, int w, int h) { - UINT swp_flags = SWP_DEFERERASE | - SWP_NOACTIVATE | - SWP_NOCOPYBITS | - SWP_NOOWNERZORDER | - SWP_NOZORDER; - gfx::Rect bounds = gfx::win::DIPToScreenRect(gfx::Rect(x,y,w,h)); - - // Only send the SHOWWINDOW flag if we're invisible, to avoid flashing. - if (!IsWindowVisible(host_->native_view())) - swp_flags = (swp_flags | SWP_SHOWWINDOW) & ~SWP_NOREDRAW; - - if (host_->fast_resize()) { - // In a fast resize, we move the window and clip it with SetWindowRgn. - RECT win_rect; - GetWindowRect(host_->native_view(), &win_rect); - gfx::Rect rect(win_rect); - SetWindowPos(host_->native_view(), 0, bounds.x(), bounds.y(), - rect.width(), rect.height(), - swp_flags); - - InstallClip(0, 0, bounds.width(), bounds.height()); - } else { - SetWindowPos(host_->native_view(), 0, bounds.x(), bounds.y(), - bounds.width(), bounds.height(), swp_flags); - } -} - -void NativeViewHostWin::HideWidget() { - if (!IsWindowVisible(host_->native_view())) - return; // Currently not visible, nothing to do. - - // The window is currently visible, but its clipped by another view. Hide - // it. - SetWindowPos(host_->native_view(), 0, 0, 0, 0, 0, - SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | - SWP_NOREDRAW | SWP_NOOWNERZORDER); -} - -void NativeViewHostWin::SetFocus() { - ::SetFocus(host_->native_view()); -} - -gfx::NativeViewAccessible NativeViewHostWin::GetNativeViewAccessible() { - HWND hwnd = host_->native_view(); - if (!IsWindow(hwnd)) - return NULL; - - IAccessible* accessible = NULL; - HRESULT success = ::AccessibleObjectFromWindow( - hwnd, OBJID_CLIENT, IID_IAccessible, - reinterpret_cast<void**>(&accessible)); - - if (success == S_OK) { - return accessible; - } else { - return NULL; - } -} - -//////////////////////////////////////////////////////////////////////////////// -// NativeViewHostWrapper, public: - -// static -NativeViewHostWrapper* NativeViewHostWrapper::CreateWrapper( - NativeViewHost* host) { - return new NativeViewHostWin(host); -} - -} // namespace views diff --git a/chromium/ui/views/controls/native/native_view_host_win.h b/chromium/ui/views/controls/native/native_view_host_win.h deleted file mode 100644 index ea94b351ea3..00000000000 --- a/chromium/ui/views/controls/native/native_view_host_win.h +++ /dev/null @@ -1,47 +0,0 @@ -// 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_CONTROLS_NATIVE_NATIVE_VIEW_HOST_WIN_H_ -#define UI_VIEWS_CONTROLS_NATIVE_NATIVE_VIEW_HOST_WIN_H_ - -#include "base/basictypes.h" -#include "ui/views/controls/native/native_view_host_wrapper.h" - -namespace views { - -class NativeViewHost; - -// A Windows implementation of NativeViewHostWrapper -class NativeViewHostWin : public NativeViewHostWrapper { - public: - explicit NativeViewHostWin(NativeViewHost* host); - virtual ~NativeViewHostWin(); - - // Overridden from NativeViewHostWrapper: - virtual void NativeViewWillAttach(); - virtual void NativeViewDetaching(bool destroyed); - virtual void AddedToWidget(); - virtual void RemovedFromWidget(); - virtual void InstallClip(int x, int y, int w, int h); - virtual bool HasInstalledClip(); - virtual void UninstallClip(); - virtual void ShowWidget(int x, int y, int w, int h); - virtual void HideWidget(); - virtual void SetFocus(); - virtual gfx::NativeViewAccessible GetNativeViewAccessible(); - - private: - // Our associated NativeViewHost. - NativeViewHost* host_; - - // Have we installed a region on the gfx::NativeView used to clip to only the - // visible portion of the gfx::NativeView ? - bool installed_clip_; - - DISALLOW_COPY_AND_ASSIGN(NativeViewHostWin); -}; - -} // namespace views - -#endif // UI_VIEWS_CONTROLS_NATIVE_NATIVE_VIEW_HOST_WIN_H_ diff --git a/chromium/ui/views/controls/native/native_view_host_wrapper.h b/chromium/ui/views/controls/native/native_view_host_wrapper.h index 3b3cb37fbdc..306aa0148bb 100644 --- a/chromium/ui/views/controls/native/native_view_host_wrapper.h +++ b/chromium/ui/views/controls/native/native_view_host_wrapper.h @@ -62,6 +62,10 @@ class VIEWS_EXPORT NativeViewHostWrapper { // view. virtual gfx::NativeViewAccessible GetNativeViewAccessible() = 0; + // Returns the native cursor corresponding to the point (x, y) + // in the native view. + virtual gfx::NativeCursor GetCursor(int x, int y) = 0; + // Creates a platform-specific instance of an object implementing this // interface. static NativeViewHostWrapper* CreateWrapper(NativeViewHost* host); diff --git a/chromium/ui/views/controls/native_control.cc b/chromium/ui/views/controls/native_control.cc deleted file mode 100644 index faebbc81f90..00000000000 --- a/chromium/ui/views/controls/native_control.cc +++ /dev/null @@ -1,389 +0,0 @@ -// 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/controls/native_control.h" - -#include <atlbase.h> -#include <atlapp.h> -#include <atlcrack.h> -#include <atlframe.h> -#include <atlmisc.h> - -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "ui/base/accessibility/accessibility_types.h" -#include "ui/base/l10n/l10n_util_win.h" -#include "ui/base/view_prop.h" -#include "ui/events/keycodes/keyboard_code_conversion_win.h" -#include "ui/events/keycodes/keyboard_codes.h" -#include "ui/gfx/win/hwnd_util.h" -#include "ui/views/background.h" -#include "ui/views/controls/native/native_view_host.h" -#include "ui/views/focus/focus_manager.h" -#include "ui/views/widget/widget.h" - -using ui::ViewProp; - -namespace views { - -// Maps to the NativeControl. -static const char* const kNativeControlKey = "__NATIVE_CONTROL__"; - -class NativeControlContainer : public CWindowImpl<NativeControlContainer, - CWindow, - CWinTraits<WS_CHILD | WS_CLIPSIBLINGS | - WS_CLIPCHILDREN>> { - public: - explicit NativeControlContainer(NativeControl* parent) - : parent_(parent), - control_(NULL), - original_handler_(NULL) { - } - - void Init() { - Create(parent_->GetWidget()->GetNativeView()); - ::ShowWindow(m_hWnd, SW_SHOW); - } - - virtual ~NativeControlContainer() { - } - - // NOTE: If you add a new message, be sure and verify parent_ is valid before - // calling into parent_. - DECLARE_FRAME_WND_CLASS(L"ChromeViewsNativeControlContainer", NULL); - BEGIN_MSG_MAP(NativeControlContainer); - MSG_WM_CREATE(OnCreate); - MSG_WM_ERASEBKGND(OnEraseBkgnd); - MSG_WM_PAINT(OnPaint); - MSG_WM_SIZE(OnSize); - MSG_WM_NOTIFY(OnNotify); - MSG_WM_COMMAND(OnCommand); - MSG_WM_DESTROY(OnDestroy); - MSG_WM_CONTEXTMENU(OnContextMenu); - MSG_WM_CTLCOLORBTN(OnCtlColorBtn); - MSG_WM_CTLCOLORSTATIC(OnCtlColorStatic) - END_MSG_MAP(); - - HWND GetControl() { - return control_; - } - - // Called when the parent is getting deleted. This control stays around until - // it gets the OnFinalMessage call. - void ResetParent() { - parent_ = NULL; - } - - void OnFinalMessage(HWND hwnd) { - if (parent_) - parent_->NativeControlDestroyed(); - delete this; - } - - private: - friend class NativeControl; - - LRESULT OnCreate(LPCREATESTRUCT create_struct) { - control_ = parent_->CreateNativeControl(m_hWnd); - - // We subclass the control hwnd so we get the WM_KEYDOWN messages. - original_handler_ = gfx::SetWindowProc( - control_, &NativeControl::NativeControlWndProc); - prop_.reset(new ViewProp(control_, kNativeControlKey , parent_)); - - ::ShowWindow(control_, SW_SHOW); - return 1; - } - - LRESULT OnEraseBkgnd(HDC dc) { - return 1; - } - - void OnPaint(HDC ignore) { - PAINTSTRUCT ps; - HDC dc = ::BeginPaint(*this, &ps); - ::EndPaint(*this, &ps); - } - - void OnSize(int type, const CSize& sz) { - ::MoveWindow(control_, 0, 0, sz.cx, sz.cy, TRUE); - } - - LRESULT OnCommand(UINT code, int id, HWND source) { - return parent_ ? parent_->OnCommand(code, id, source) : 0; - } - - LRESULT OnNotify(int w_param, LPNMHDR l_param) { - if (parent_) - return parent_->OnNotify(w_param, l_param); - else - return 0; - } - - void OnDestroy() { - if (parent_) - parent_->OnDestroy(); - } - - void OnContextMenu(HWND window, const POINT& location) { - if (parent_) - parent_->OnContextMenu(location); - } - - // We need to find an ancestor with a non-null background, and - // ask it for a (solid color) brush that approximates - // the background. The caller will use this when drawing - // the native control as a background color, particularly - // for radiobuttons and XP style pushbuttons. - LRESULT OnCtlColor(UINT msg, HDC dc, HWND control) { - const View *ancestor = parent_; - while (ancestor) { - const Background *background = ancestor->background(); - if (background) { - HBRUSH brush = background->GetNativeControlBrush(); - if (brush) - return reinterpret_cast<LRESULT>(brush); - } - ancestor = ancestor->parent(); - } - - // COLOR_BTNFACE is the default for dialog box backgrounds. - return reinterpret_cast<LRESULT>(GetSysColorBrush(COLOR_BTNFACE)); - } - - LRESULT OnCtlColorBtn(HDC dc, HWND control) { - return OnCtlColor(WM_CTLCOLORBTN, dc, control); - } - - LRESULT OnCtlColorStatic(HDC dc, HWND control) { - return OnCtlColor(WM_CTLCOLORSTATIC, dc, control); - } - - NativeControl* parent_; - HWND control_; - - // Message handler that was set before we reset it. - WNDPROC original_handler_; - - scoped_ptr<ViewProp> prop_; - - DISALLOW_COPY_AND_ASSIGN(NativeControlContainer); -}; - -NativeControl::NativeControl() : hwnd_view_(NULL), - container_(NULL), - fixed_width_(-1), - horizontal_alignment_(CENTER), - fixed_height_(-1), - vertical_alignment_(CENTER) { - SetFocusable(true); -} - -NativeControl::~NativeControl() { - if (container_) { - container_->ResetParent(); - ::DestroyWindow(*container_); - } -} - -void NativeControl::ValidateNativeControl() { - if (hwnd_view_ == NULL) { - hwnd_view_ = new NativeViewHost; - AddChildView(hwnd_view_); - } - - if (!container_ && visible()) { - container_ = new NativeControlContainer(this); - container_->Init(); - hwnd_view_->Attach(*container_); - if (!enabled()) - EnableWindow(GetNativeControlHWND(), enabled()); - - // This message ensures that the focus border is shown. - ::SendMessage(container_->GetControl(), - WM_CHANGEUISTATE, - MAKELPARAM(UIS_CLEAR, UISF_HIDEFOCUS), - 0); - } -} - -void NativeControl::ViewHierarchyChanged( - const ViewHierarchyChangedDetails& details) { - if (details.is_add && details.parent != this && !container_ && GetWidget()) { - ValidateNativeControl(); - Layout(); - } -} - -void NativeControl::Layout() { - if (!container_ && GetWidget()) - ValidateNativeControl(); - - if (hwnd_view_) { - gfx::Rect lb = GetLocalBounds(); - - int x = lb.x(); - int y = lb.y(); - int width = lb.width(); - int height = lb.height(); - if (fixed_width_ > 0) { - width = std::min(fixed_width_, width); - switch (horizontal_alignment_) { - case LEADING: - // Nothing to do. - break; - case CENTER: - x += (lb.width() - width) / 2; - break; - case TRAILING: - x = x + lb.width() - width; - break; - default: - NOTREACHED(); - } - } - - if (fixed_height_ > 0) { - height = std::min(fixed_height_, height); - switch (vertical_alignment_) { - case LEADING: - // Nothing to do. - break; - case CENTER: - y += (lb.height() - height) / 2; - break; - case TRAILING: - y = y + lb.height() - height; - break; - default: - NOTREACHED(); - } - } - - hwnd_view_->SetBounds(x, y, width, height); - } -} - -void NativeControl::OnContextMenu(const POINT& location) { - if (!context_menu_controller()) - return; - - if (location.x == -1 && location.y == -1) { - ShowContextMenu(GetKeyboardContextMenuLocation(), - ui::MENU_SOURCE_KEYBOARD); - } else { - ShowContextMenu(gfx::Point(location), ui::MENU_SOURCE_MOUSE); - } -} - -void NativeControl::OnFocus() { - if (container_) { - DCHECK(container_->GetControl()); - ::SetFocus(container_->GetControl()); - NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_FOCUS, false); - } -} - -HWND NativeControl::GetNativeControlHWND() { - if (container_) - return container_->GetControl(); - else - return NULL; -} - -void NativeControl::NativeControlDestroyed() { - if (hwnd_view_) - hwnd_view_->Detach(); - container_ = NULL; -} - -void NativeControl::SetVisible(bool is_visible) { - if (is_visible != visible()) { - View::SetVisible(is_visible); - if (!is_visible && container_) - ::DestroyWindow(*container_); - else if (is_visible && !container_) - ValidateNativeControl(); - } -} - -void NativeControl::OnEnabledChanged() { - View::OnEnabledChanged(); - if (GetNativeControlHWND()) - EnableWindow(GetNativeControlHWND(), enabled()); -} - -void NativeControl::OnPaint(gfx::Canvas* canvas) { -} - -void NativeControl::VisibilityChanged(View* starting_from, bool is_visible) { - SetVisible(is_visible); -} - -void NativeControl::SetFixedWidth(int width, Alignment alignment) { - DCHECK_GT(width, 0); - fixed_width_ = width; - horizontal_alignment_ = alignment; -} - -void NativeControl::SetFixedHeight(int height, Alignment alignment) { - DCHECK_GT(height, 0); - fixed_height_ = height; - vertical_alignment_ = alignment; -} - -DWORD NativeControl::GetAdditionalExStyle() const { - // If the UI for the view is mirrored, we should make sure we add the - // extended window style for a right-to-left layout so the subclass creates - // a mirrored HWND for the underlying control. - DWORD ex_style = 0; - if (base::i18n::IsRTL()) - ex_style |= l10n_util::GetExtendedStyles(); - - return ex_style; -} - -DWORD NativeControl::GetAdditionalRTLStyle() const { - // If the UI for the view is mirrored, we should make sure we add the - // extended window style for a right-to-left layout so the subclass creates - // a mirrored HWND for the underlying control. - DWORD ex_style = 0; - if (base::i18n::IsRTL()) - ex_style |= l10n_util::GetExtendedTooltipStyles(); - - return ex_style; -} - -// static -LRESULT CALLBACK NativeControl::NativeControlWndProc(HWND window, - UINT message, - WPARAM w_param, - LPARAM l_param) { - NativeControl* native_control = static_cast<NativeControl*>( - ViewProp::GetValue(window, kNativeControlKey)); - DCHECK(native_control); - WNDPROC original_handler = native_control->container_->original_handler_; - DCHECK(original_handler); - - if (message == WM_KEYDOWN && - native_control->OnKeyDown(ui::KeyboardCodeForWindowsKeyCode(w_param))) { - return 0; - } else if (message == WM_SETFOCUS) { - // Let the focus manager know that the focus changed. - FocusManager* focus_manager = native_control->GetFocusManager(); - if (focus_manager) { - focus_manager->SetFocusedView(native_control); - } else { - NOTREACHED(); - } - } else if (message == WM_DESTROY) { - gfx::SetWindowProc(window, reinterpret_cast<WNDPROC>(original_handler)); - native_control->container_->prop_.reset(); - } - - return CallWindowProc(reinterpret_cast<WNDPROC>(original_handler), window, - message, w_param, l_param); -} - -} // namespace views diff --git a/chromium/ui/views/controls/native_control.h b/chromium/ui/views/controls/native_control.h deleted file mode 100644 index 80aa5887cf1..00000000000 --- a/chromium/ui/views/controls/native_control.h +++ /dev/null @@ -1,126 +0,0 @@ -// 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_CONTROLS_NATIVE_CONTROL_H_ -#define UI_VIEWS_CONTROLS_NATIVE_CONTROL_H_ - -#include <windows.h> - -#include "ui/events/keycodes/keyboard_codes.h" -#include "ui/views/view.h" - -namespace views { - -class NativeViewHost; -class NativeControlContainer; - -//////////////////////////////////////////////////////////////////////////////// -// -// NativeControl is an abstract view that is used to implement views wrapping -// native controls. Subclasses can simply implement CreateNativeControl() to -// wrap a new kind of control -// -//////////////////////////////////////////////////////////////////////////////// -class VIEWS_EXPORT NativeControl : public View { - public: - enum Alignment { - LEADING = 0, - CENTER, - TRAILING }; - - NativeControl(); - virtual ~NativeControl(); - - virtual void ViewHierarchyChanged( - const ViewHierarchyChangedDetails& details) OVERRIDE; - virtual void Layout(); - - // Overridden to properly set the native control state. - virtual void SetVisible(bool f); - virtual void OnEnabledChanged(); - - // Overridden to do nothing. - virtual void OnPaint(gfx::Canvas* canvas); - - protected: - friend class NativeControlContainer; - - // Overridden by sub-classes to create the windows control which is wrapped - virtual HWND CreateNativeControl(HWND parent_container) = 0; - - // Invoked when the native control sends a WM_NOTIFY message to its parent - virtual LRESULT OnNotify(int w_param, LPNMHDR l_param) = 0; - - // Invoked when the native control sends a WM_COMMAND message to its parent - virtual LRESULT OnCommand(UINT code, int id, HWND source) { return 0; } - - // Invoked when the appropriate gesture for a context menu is issued. - virtual void OnContextMenu(const POINT& location); - - // Overridden so to set the native focus to the native control. - virtual void OnFocus(); - - // Invoked when the native control sends a WM_DESTORY message to its parent. - virtual void OnDestroy() { } - - // Return the native control - virtual HWND GetNativeControlHWND(); - - // Invoked by the native windows control when it has been destroyed. This is - // invoked AFTER WM_DESTORY has been sent. Any window commands send to the - // HWND will most likely fail. - void NativeControlDestroyed(); - - // Overridden so that the control properly reflects parent's visibility. - virtual void VisibilityChanged(View* starting_from, bool is_visible); - - // Controls that have fixed sizes should call these methods to specify the - // actual size and how they should be aligned within their parent. - void SetFixedWidth(int width, Alignment alignment); - void SetFixedHeight(int height, Alignment alignment); - - // Invoked when a key is pressed on the control. - // Should return true if the key message was processed, false otherwise. - virtual bool OnKeyDown(ui::KeyboardCode virtual_key_code) { return false; } - - // Returns additional extended style flags. When subclasses call - // CreateWindowEx in order to create the underlying control, they must OR the - // ExStyle parameter with the value returned by this function. - // - // We currently use this method in order to add flags such as WS_EX_LAYOUTRTL - // to the HWND for views with right-to-left UI layout. - DWORD GetAdditionalExStyle() const; - - // TODO(xji): we use the following temporary function as we transition the - // various native controls to use the right set of RTL flags. This function - // will go away (and be replaced by GetAdditionalExStyle()) once all the - // controls are properly transitioned. - DWORD GetAdditionalRTLStyle() const; - - // This variable is protected to provide subclassers direct access. However - // subclassers should always check for NULL since this variable is only - // initialized in ValidateNativeControl(). - NativeViewHost* hwnd_view_; - - // Fixed size information. -1 for a size means no fixed size. - int fixed_width_; - Alignment horizontal_alignment_; - int fixed_height_; - Alignment vertical_alignment_; - - private: - - void ValidateNativeControl(); - - static LRESULT CALLBACK NativeControlWndProc(HWND window, UINT message, - WPARAM w_param, LPARAM l_param); - - NativeControlContainer* container_; - - DISALLOW_COPY_AND_ASSIGN(NativeControl); -}; - -} // namespace views - -#endif // UI_VIEWS_CONTROLS_NATIVE_CONTROL_H_ diff --git a/chromium/ui/views/controls/native_control_win.cc b/chromium/ui/views/controls/native_control_win.cc deleted file mode 100644 index c50411d13f5..00000000000 --- a/chromium/ui/views/controls/native_control_win.cc +++ /dev/null @@ -1,226 +0,0 @@ -// 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/controls/native_control_win.h" - -#include <windowsx.h> - -#include "base/logging.h" -#include "ui/base/accessibility/accessibility_types.h" -#include "ui/base/l10n/l10n_util_win.h" -#include "ui/base/view_prop.h" -#include "ui/gfx/win/hwnd_util.h" -#include "ui/views/controls/combobox/combobox.h" -#include "ui/views/focus/focus_manager.h" -#include "ui/views/widget/widget.h" - -using ui::ViewProp; - -const char kNativeControlWinKey[] = "__NATIVE_CONTROL_WIN__"; - -namespace views { - -//////////////////////////////////////////////////////////////////////////////// -// NativeControlWin, public: - -NativeControlWin::NativeControlWin() : original_wndproc_(NULL) { -} - -NativeControlWin::~NativeControlWin() { - HWND hwnd = native_view(); - if (hwnd) { - // Destroy the hwnd if it still exists. Otherwise we won't have shut things - // down correctly, leading to leaking and crashing if another message - // comes in for the hwnd. - Detach(); - DestroyWindow(hwnd); - } -} - -bool NativeControlWin::ProcessMessage(UINT message, - WPARAM w_param, - LPARAM l_param, - LRESULT* result) { - switch (message) { - case WM_CONTEXTMENU: - ShowContextMenu(gfx::Point(GET_X_LPARAM(l_param), GET_Y_LPARAM(l_param))); - *result = 0; - return true; - case WM_CTLCOLORBTN: - case WM_CTLCOLORSTATIC: - *result = GetControlColor(message, reinterpret_cast<HDC>(w_param), - native_view()); - return true; - } - return false; -} - -//////////////////////////////////////////////////////////////////////////////// -// NativeControlWin, View overrides: - -void NativeControlWin::OnEnabledChanged() { - View::OnEnabledChanged(); - if (native_view()) - EnableWindow(native_view(), enabled()); -} - -void NativeControlWin::ViewHierarchyChanged( - const ViewHierarchyChangedDetails& details) { - // Call the base class to hide the view if we're being removed. - NativeViewHost::ViewHierarchyChanged(details); - - // Create the HWND when we're added to a valid Widget. Many controls need a - // parent HWND to function properly. - if (details.is_add && GetWidget() && !native_view()) - CreateNativeControl(); -} - -void NativeControlWin::VisibilityChanged(View* starting_from, bool is_visible) { - // We might get called due to visibility changes at any point in the - // hierarchy, lets check whether we are really visible or not. - bool is_drawn = IsDrawn(); - if (!is_drawn && native_view()) { - // We destroy the child control HWND when we become invisible because of the - // performance cost of maintaining many HWNDs. - HWND hwnd = native_view(); - Detach(); - DestroyWindow(hwnd); - } else if (is_drawn && !native_view()) { - if (GetWidget()) - CreateNativeControl(); - } - if (is_drawn) { - // The view becomes visible after native control is created. - // Layout now. - Layout(); - } -} - -void NativeControlWin::OnFocus() { - DCHECK(native_view()); - SetFocus(native_view()); - - // Since we are being wrapped by a view, accessibility should receive - // the super class as the focused view. - View* parent_view = parent(); - - // Due to some controls not behaving as expected without having - // a native win32 control, we don't always send a native (MSAA) - // focus notification. - bool send_native_event = - strcmp(parent_view->GetClassName(), Combobox::kViewClassName) && - parent_view->HasFocus(); - - // Send the accessibility focus notification. - parent_view->NotifyAccessibilityEvent( - ui::AccessibilityTypes::EVENT_FOCUS, send_native_event); -} - -//////////////////////////////////////////////////////////////////////////////// -// NativeControlWin, protected: - -void NativeControlWin::ShowContextMenu(const gfx::Point& location) { - if (!context_menu_controller()) - return; - - if (location.x() == -1 && location.y() == -1) { - View::ShowContextMenu(GetKeyboardContextMenuLocation(), - ui::MENU_SOURCE_KEYBOARD); - } else { - View::ShowContextMenu(location, ui::MENU_SOURCE_MOUSE); - } -} - -void NativeControlWin::NativeControlCreated(HWND native_control) { - // Associate this object with the control's HWND so that NativeWidgetWin can - // find this object when it receives messages from it. - props_.push_back(new ViewProp(native_control, kNativeControlWinKey, this)); - props_.push_back(ChildWindowMessageProcessor::Register(native_control, this)); - - // Subclass so we get WM_KEYDOWN and WM_SETFOCUS messages. - original_wndproc_ = gfx::SetWindowProc( - native_control, &NativeControlWin::NativeControlWndProc); - - Attach(native_control); - // native_view() is now valid. - - // Update the newly created HWND with any resident enabled state. - EnableWindow(native_view(), enabled()); - - // This message ensures that the focus border is shown. - SendMessage(native_view(), WM_CHANGEUISTATE, - MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0); -} - -DWORD NativeControlWin::GetAdditionalExStyle() const { - // If the UI for the view is mirrored, we should make sure we add the - // extended window style for a right-to-left layout so the subclass creates - // a mirrored HWND for the underlying control. - DWORD ex_style = 0; - if (base::i18n::IsRTL()) - ex_style |= l10n_util::GetExtendedStyles(); - - return ex_style; -} - -DWORD NativeControlWin::GetAdditionalRTLStyle() const { - // If the UI for the view is mirrored, we should make sure we add the - // extended window style for a right-to-left layout so the subclass creates - // a mirrored HWND for the underlying control. - DWORD ex_style = 0; - if (base::i18n::IsRTL()) - ex_style |= l10n_util::GetExtendedTooltipStyles(); - - return ex_style; -} - -//////////////////////////////////////////////////////////////////////////////// -// NativeControlWin, private: - -LRESULT NativeControlWin::GetControlColor(UINT message, HDC dc, HWND sender) { - View *ancestor = this; - while (ancestor) { - const Background* background = ancestor->background(); - if (background) { - HBRUSH brush = background->GetNativeControlBrush(); - if (brush) - return reinterpret_cast<LRESULT>(brush); - } - ancestor = ancestor->parent(); - } - - // COLOR_BTNFACE is the default for dialog box backgrounds. - return reinterpret_cast<LRESULT>(GetSysColorBrush(COLOR_BTNFACE)); -} - -// static -LRESULT NativeControlWin::NativeControlWndProc(HWND window, - UINT message, - WPARAM w_param, - LPARAM l_param) { - NativeControlWin* native_control = reinterpret_cast<NativeControlWin*>( - ViewProp::GetValue(window, kNativeControlWinKey)); - DCHECK(native_control); - - if (message == WM_KEYDOWN && - native_control->OnKeyDown(static_cast<int>(w_param))) { - return 0; - } else if (message == WM_SETFOCUS) { - // Let the focus manager know that the focus changed. - FocusManager* focus_manager = native_control->GetFocusManager(); - if (focus_manager) { - focus_manager->SetFocusedView(native_control->focus_view()); - } else { - NOTREACHED(); - } - } else if (message == WM_DESTROY) { - native_control->props_.clear(); - gfx::SetWindowProc(window, native_control->original_wndproc_); - } - - return CallWindowProc(native_control->original_wndproc_, window, message, - w_param, l_param); -} - -} // namespace views diff --git a/chromium/ui/views/controls/native_control_win.h b/chromium/ui/views/controls/native_control_win.h deleted file mode 100644 index 3ee5681f1dc..00000000000 --- a/chromium/ui/views/controls/native_control_win.h +++ /dev/null @@ -1,99 +0,0 @@ -// 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_CONTROLS_NATIVE_CONTROL_WIN_H_ -#define UI_VIEWS_CONTROLS_NATIVE_CONTROL_WIN_H_ - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/memory/scoped_vector.h" -#include "ui/views/controls/native/native_view_host.h" -#include "ui/views/widget/child_window_message_processor.h" - -namespace ui { -class ViewProp; -} - -namespace views { - -// A View that hosts a native Windows control. -class NativeControlWin : public ChildWindowMessageProcessor, - public NativeViewHost { - public: - NativeControlWin(); - virtual ~NativeControlWin(); - - // Called by our subclassed window procedure when a WM_KEYDOWN message is - // received by the HWND created by an object derived from NativeControlWin. - // Returns true if the key was processed, false otherwise. - virtual bool OnKeyDown(int vkey) { return false; } - - // Overridden from ChildWindowMessageProcessor: - virtual bool ProcessMessage(UINT message, - WPARAM w_param, - LPARAM l_param, - LRESULT* result) OVERRIDE; - - // Overridden from View: - virtual void OnEnabledChanged() OVERRIDE; - - protected: - virtual void ViewHierarchyChanged( - const ViewHierarchyChangedDetails& details) OVERRIDE; - virtual void VisibilityChanged(View* starting_from, bool is_visible) OVERRIDE; - virtual void OnFocus() OVERRIDE; - - // Called by the containing NativeWidgetWin when a WM_CONTEXTMENU message is - // received from the HWND created by an object derived from NativeControlWin. - virtual void ShowContextMenu(const gfx::Point& location); - - // Called when the NativeControlWin is attached to a View hierarchy with a - // valid Widget. The NativeControlWin should use this opportunity to create - // its associated HWND. - virtual void CreateNativeControl() = 0; - - // MUST be called by the subclass implementation of |CreateNativeControl| - // immediately after creating the control HWND, otherwise it won't be attached - // to the NativeViewHost and will be effectively orphaned. - virtual void NativeControlCreated(HWND native_control); - - // Returns additional extended style flags. When subclasses call - // CreateWindowEx in order to create the underlying control, they must OR the - // ExStyle parameter with the value returned by this function. - // - // We currently use this method in order to add flags such as WS_EX_LAYOUTRTL - // to the HWND for views with right-to-left UI layout. - DWORD GetAdditionalExStyle() const; - - // TODO(xji): we use the following temporary function as we transition the - // various native controls to use the right set of RTL flags. This function - // will go away (and be replaced by GetAdditionalExStyle()) once all the - // controls are properly transitioned. - DWORD GetAdditionalRTLStyle() const; - - private: - typedef ScopedVector<ui::ViewProp> ViewProps; - - // Called by the containing NativeWidgetWin when a message of type - // WM_CTLCOLORBTN or WM_CTLCOLORSTATIC is sent from the HWND created by an - // object derived from NativeControlWin. - LRESULT GetControlColor(UINT message, HDC dc, HWND sender); - - // Our subclass window procedure for the attached control. - static LRESULT CALLBACK NativeControlWndProc(HWND window, - UINT message, - WPARAM w_param, - LPARAM l_param); - - // The window procedure before we subclassed. - WNDPROC original_wndproc_; - - ViewProps props_; - - DISALLOW_COPY_AND_ASSIGN(NativeControlWin); -}; - -} // namespace views - -#endif // UI_VIEWS_CONTROLS_NATIVE_CONTROL_WIN_H_ diff --git a/chromium/ui/views/controls/prefix_delegate.h b/chromium/ui/views/controls/prefix_delegate.h index 41d30d43cc8..07d85989f0e 100644 --- a/chromium/ui/views/controls/prefix_delegate.h +++ b/chromium/ui/views/controls/prefix_delegate.h @@ -23,7 +23,7 @@ class VIEWS_EXPORT PrefixDelegate : public View { virtual void SetSelectedRow(int row) = 0; // Returns the item at the specified row. - virtual string16 GetTextForRow(int row) = 0; + virtual base::string16 GetTextForRow(int row) = 0; protected: virtual ~PrefixDelegate() {} diff --git a/chromium/ui/views/controls/prefix_selector.cc b/chromium/ui/views/controls/prefix_selector.cc index d66a0346c84..c901edd2c11 100644 --- a/chromium/ui/views/controls/prefix_selector.cc +++ b/chromium/ui/views/controls/prefix_selector.cc @@ -47,12 +47,12 @@ void PrefixSelector::ConfirmCompositionText() { void PrefixSelector::ClearCompositionText() { } -void PrefixSelector::InsertText(const string16& text) { +void PrefixSelector::InsertText(const base::string16& text) { OnTextInput(text); } -void PrefixSelector::InsertChar(char16 ch, int flags) { - OnTextInput(string16(1, ch)); +void PrefixSelector::InsertChar(base::char16 ch, int flags) { + OnTextInput(base::string16(1, ch)); } gfx::NativeWindow PrefixSelector::GetAttachedWindow() const { @@ -115,7 +115,7 @@ bool PrefixSelector::DeleteRange(const gfx::Range& range) { } bool PrefixSelector::GetTextFromRange(const gfx::Range& range, - string16* text) const { + base::string16* text) const { return false; } @@ -143,7 +143,14 @@ void PrefixSelector::OnCandidateWindowUpdated() { void PrefixSelector::OnCandidateWindowHidden() { } -void PrefixSelector::OnTextInput(const string16& text) { +bool PrefixSelector::IsEditingCommandEnabled(int command_id) { + return false; +} + +void PrefixSelector::ExecuteEditingCommand(int command_id) { +} + +void PrefixSelector::OnTextInput(const base::string16& text) { // Small hack to filter out 'tab' and 'enter' input, as the expectation is // that they are control characters and will not affect the currently-active // prefix. @@ -171,9 +178,9 @@ void PrefixSelector::OnTextInput(const string16& text) { time_of_last_key_ = now; const int start_row = row; - const string16 lower_text(base::i18n::ToLower(current_text_)); + const base::string16 lower_text(base::i18n::ToLower(current_text_)); do { - if (TextAtRowMatchesText(row, current_text_)) { + if (TextAtRowMatchesText(row, lower_text)) { prefix_delegate_->SetSelectedRow(row); return; } @@ -182,8 +189,8 @@ void PrefixSelector::OnTextInput(const string16& text) { } bool PrefixSelector::TextAtRowMatchesText(int row, - const string16& lower_text) { - const string16 model_text( + const base::string16& lower_text) { + const base::string16 model_text( base::i18n::ToLower(prefix_delegate_->GetTextForRow(row))); return (model_text.size() >= lower_text.size()) && (model_text.compare(0, lower_text.size(), lower_text) == 0); diff --git a/chromium/ui/views/controls/prefix_selector.h b/chromium/ui/views/controls/prefix_selector.h index 57f6dcfe183..255046f60df 100644 --- a/chromium/ui/views/controls/prefix_selector.h +++ b/chromium/ui/views/controls/prefix_selector.h @@ -29,8 +29,8 @@ class VIEWS_EXPORT PrefixSelector : public ui::TextInputClient { const ui::CompositionText& composition) OVERRIDE; virtual void ConfirmCompositionText() OVERRIDE; virtual void ClearCompositionText() OVERRIDE; - virtual void InsertText(const string16& text) OVERRIDE; - virtual void InsertChar(char16 ch, int flags) OVERRIDE; + virtual void InsertText(const base::string16& text) OVERRIDE; + virtual void InsertChar(base::char16 ch, int flags) OVERRIDE; virtual gfx::NativeWindow GetAttachedWindow() const OVERRIDE; virtual ui::TextInputType GetTextInputType() const OVERRIDE; virtual ui::TextInputMode GetTextInputMode() const OVERRIDE; @@ -45,7 +45,7 @@ class VIEWS_EXPORT PrefixSelector : public ui::TextInputClient { virtual bool SetSelectionRange(const gfx::Range& range) OVERRIDE; virtual bool DeleteRange(const gfx::Range& range) OVERRIDE; virtual bool GetTextFromRange(const gfx::Range& range, - string16* text) const OVERRIDE; + base::string16* text) const OVERRIDE; virtual void OnInputMethodChanged() OVERRIDE; virtual bool ChangeTextDirectionAndLayoutAlignment( base::i18n::TextDirection direction) OVERRIDE; @@ -55,12 +55,15 @@ class VIEWS_EXPORT PrefixSelector : public ui::TextInputClient { virtual void OnCandidateWindowUpdated() OVERRIDE; virtual void OnCandidateWindowHidden() OVERRIDE; + virtual bool IsEditingCommandEnabled(int command_id) OVERRIDE; + virtual void ExecuteEditingCommand(int command_id) OVERRIDE; + private: // Invoked when text is typed. Tries to change the selection appropriately. - void OnTextInput(const string16& text); + void OnTextInput(const base::string16& text); // Returns true if the text of the node at |row| starts with |lower_text|. - bool TextAtRowMatchesText(int row, const string16& lower_text); + bool TextAtRowMatchesText(int row, const base::string16& lower_text); // Clears |current_text_| and resets |time_of_last_key_|. void ClearText(); @@ -70,7 +73,7 @@ class VIEWS_EXPORT PrefixSelector : public ui::TextInputClient { // Time OnTextInput() was last invoked. base::TimeTicks time_of_last_key_; - string16 current_text_; + base::string16 current_text_; DISALLOW_COPY_AND_ASSIGN(PrefixSelector); }; diff --git a/chromium/ui/views/controls/prefix_selector_unittest.cc b/chromium/ui/views/controls/prefix_selector_unittest.cc index c39e6cc652a..031c0079aee 100644 --- a/chromium/ui/views/controls/prefix_selector_unittest.cc +++ b/chromium/ui/views/controls/prefix_selector_unittest.cc @@ -11,6 +11,8 @@ #include "ui/views/controls/prefix_delegate.h" #include "ui/views/test/views_test_base.h" +using base::ASCIIToUTF16; + namespace views { class TestPrefixDelegate : public PrefixDelegate { @@ -36,12 +38,12 @@ class TestPrefixDelegate : public PrefixDelegate { selected_row_ = row; } - virtual string16 GetTextForRow(int row) OVERRIDE { + virtual base::string16 GetTextForRow(int row) OVERRIDE { return rows_[row]; } private: - std::vector<string16> rows_; + std::vector<base::string16> rows_; int selected_row_; DISALLOW_COPY_AND_ASSIGN(TestPrefixDelegate); diff --git a/chromium/ui/views/controls/progress_bar.cc b/chromium/ui/views/controls/progress_bar.cc index 087c2df9830..0d6ed17f5e0 100644 --- a/chromium/ui/views/controls/progress_bar.cc +++ b/chromium/ui/views/controls/progress_bar.cc @@ -11,7 +11,7 @@ #include "third_party/skia/include/core/SkPaint.h" #include "third_party/skia/include/core/SkXfermode.h" #include "third_party/skia/include/effects/SkGradientShader.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/gfx/canvas.h" namespace { @@ -81,7 +81,7 @@ void FillRoundRect(gfx::Canvas* canvas, p[1].iset(x, y + h); } skia::RefPtr<SkShader> s = skia::AdoptRef(SkGradientShader::CreateLinear( - p, colors, points, count, SkShader::kClamp_TileMode, NULL)); + p, colors, points, count, SkShader::kClamp_TileMode)); paint.setShader(s.get()); canvas->DrawPath(path, paint); @@ -167,22 +167,23 @@ void ProgressBar::SetValue(double value) { } } -void ProgressBar::SetTooltipText(const string16& tooltip_text) { +void ProgressBar::SetTooltipText(const base::string16& tooltip_text) { tooltip_text_ = tooltip_text; } -bool ProgressBar::GetTooltipText(const gfx::Point& p, string16* tooltip) const { +bool ProgressBar::GetTooltipText(const gfx::Point& p, + base::string16* tooltip) const { DCHECK(tooltip); *tooltip = tooltip_text_; return !tooltip_text_.empty(); } -void ProgressBar::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_PROGRESSBAR; - state->state = ui::AccessibilityTypes::STATE_READONLY; +void ProgressBar::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_PROGRESS_INDICATOR; + state->AddStateFlag(ui::AX_STATE_READ_ONLY); } -gfx::Size ProgressBar::GetPreferredSize() { +gfx::Size ProgressBar::GetPreferredSize() const { gfx::Size pref_size(100, 11); gfx::Insets insets = GetInsets(); pref_size.Enlarge(insets.width(), insets.height()); @@ -227,7 +228,7 @@ void ProgressBar::OnPaint(gfx::Canvas* canvas) { kCornerRadius, 0, &inner_path); - canvas->ClipPath(inner_path); + canvas->ClipPath(inner_path, false); const SkColor bar_colors[] = { kBarTopColor, @@ -299,7 +300,7 @@ void ProgressBar::OnPaint(gfx::Canvas* canvas) { skia::RefPtr<SkShader> s = skia::AdoptRef(SkGradientShader::CreateLinear( p, highlight_colors, highlight_points, - arraysize(highlight_colors), SkShader::kClamp_TileMode, NULL)); + arraysize(highlight_colors), SkShader::kClamp_TileMode)); paint.setShader(s.get()); paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); canvas->DrawRect(gfx::Rect(highlight_left, 0, diff --git a/chromium/ui/views/controls/progress_bar.h b/chromium/ui/views/controls/progress_bar.h index 1f3d595534c..b6d3851baaf 100644 --- a/chromium/ui/views/controls/progress_bar.h +++ b/chromium/ui/views/controls/progress_bar.h @@ -35,18 +35,18 @@ class VIEWS_EXPORT ProgressBar : public View { // Sets the tooltip text. Default behavior for a progress bar is to show no // tooltip on mouse hover. Calling this lets you set a custom tooltip. To // revert to default behavior, call this with an empty string. - void SetTooltipText(const string16& tooltip_text); + void SetTooltipText(const base::string16& tooltip_text); // Overridden from View: virtual bool GetTooltipText(const gfx::Point& p, - string16* tooltip) const OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + base::string16* tooltip) const OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; private: static const char kViewClassName[]; // Overridden from View: - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual const char* GetClassName() const OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; @@ -58,7 +58,7 @@ class VIEWS_EXPORT ProgressBar : public View { double current_value_; // Tooltip text. - string16 tooltip_text_; + base::string16 tooltip_text_; DISALLOW_COPY_AND_ASSIGN(ProgressBar); }; diff --git a/chromium/ui/views/controls/progress_bar_unittest.cc b/chromium/ui/views/controls/progress_bar_unittest.cc index 78df2e17d0d..b9067b20c9b 100644 --- a/chromium/ui/views/controls/progress_bar_unittest.cc +++ b/chromium/ui/views/controls/progress_bar_unittest.cc @@ -6,16 +6,16 @@ #include "base/strings/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" namespace views { TEST(ProgressBarTest, TooltipTextProperty) { ProgressBar bar; - string16 tooltip = ASCIIToUTF16("Some text"); + base::string16 tooltip = base::ASCIIToUTF16("Some text"); EXPECT_FALSE(bar.GetTooltipText(gfx::Point(), &tooltip)); - EXPECT_EQ(string16(), tooltip); - string16 tooltip_text = ASCIIToUTF16("My progress"); + EXPECT_EQ(base::string16(), tooltip); + base::string16 tooltip_text = base::ASCIIToUTF16("My progress"); bar.SetTooltipText(tooltip_text); EXPECT_TRUE(bar.GetTooltipText(gfx::Point(), &tooltip)); EXPECT_EQ(tooltip_text, tooltip); @@ -25,11 +25,11 @@ TEST(ProgressBarTest, Accessibility) { ProgressBar bar; bar.SetValue(62); - ui::AccessibleViewState state; + ui::AXViewState state; bar.GetAccessibleState(&state); - EXPECT_EQ(ui::AccessibilityTypes::ROLE_PROGRESSBAR, state.role); - EXPECT_EQ(string16(), state.name); - EXPECT_TRUE(ui::AccessibilityTypes::STATE_READONLY & state.state); + EXPECT_EQ(ui::AX_ROLE_PROGRESS_INDICATOR, state.role); + EXPECT_EQ(base::string16(), state.name); + EXPECT_TRUE(state.HasStateFlag(ui::AX_STATE_READ_ONLY)); } } // namespace views diff --git a/chromium/ui/views/controls/resize_area.cc b/chromium/ui/views/controls/resize_area.cc index 845f2e8d42d..8a5159f7eda 100644 --- a/chromium/ui/views/controls/resize_area.cc +++ b/chromium/ui/views/controls/resize_area.cc @@ -5,13 +5,11 @@ #include "ui/views/controls/resize_area.h" #include "base/logging.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" +#include "ui/base/cursor/cursor.h" #include "ui/base/resource/resource_bundle.h" #include "ui/views/controls/resize_area_delegate.h" - -#if defined(USE_AURA) -#include "ui/base/cursor/cursor.h" -#endif +#include "ui/views/native_cursor.h" namespace views { @@ -33,14 +31,8 @@ const char* ResizeArea::GetClassName() const { } gfx::NativeCursor ResizeArea::GetCursor(const ui::MouseEvent& event) { - if (!enabled()) - return gfx::kNullCursor; -#if defined(USE_AURA) - return ui::kCursorEastWestResize; -#elif defined(OS_WIN) - static HCURSOR g_resize_cursor = LoadCursor(NULL, IDC_SIZEWE); - return g_resize_cursor; -#endif + return enabled() ? GetNativeEastWestResizeCursor() + : gfx::kNullCursor; } bool ResizeArea::OnMousePressed(const ui::MouseEvent& event) { @@ -73,8 +65,8 @@ void ResizeArea::OnMouseCaptureLost() { ReportResizeAmount(initial_position_, true); } -void ResizeArea::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_SEPARATOR; +void ResizeArea::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_SPLITTER; } void ResizeArea::ReportResizeAmount(int resize_amount, bool last_update) { diff --git a/chromium/ui/views/controls/resize_area.h b/chromium/ui/views/controls/resize_area.h index 288b912ba80..12ec310026e 100644 --- a/chromium/ui/views/controls/resize_area.h +++ b/chromium/ui/views/controls/resize_area.h @@ -32,7 +32,7 @@ class VIEWS_EXPORT ResizeArea : public View { virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE; virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE; virtual void OnMouseCaptureLost() OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; private: // Report the amount the user resized by to the delegate, accounting for diff --git a/chromium/ui/views/controls/scroll_view.cc b/chromium/ui/views/controls/scroll_view.cc index d835c1ebdf8..51db071b71f 100644 --- a/chromium/ui/views/controls/scroll_view.cc +++ b/chromium/ui/views/controls/scroll_view.cc @@ -20,22 +20,16 @@ namespace { // Subclass of ScrollView that resets the border when the theme changes. class ScrollViewWithBorder : public views::ScrollView { public: - ScrollViewWithBorder() { - SetThemeSpecificState(); - } + ScrollViewWithBorder() {} // View overrides; virtual void OnNativeThemeChanged(const ui::NativeTheme* theme) OVERRIDE { - SetThemeSpecificState(); + SetBorder(Border::CreateSolidBorder( + 1, + theme->GetSystemColor(ui::NativeTheme::kColorId_UnfocusedBorderColor))); } private: - void SetThemeSpecificState() { - set_border(Border::CreateSolidBorder( - 1, GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_UnfocusedBorderColor))); - } - DISALLOW_COPY_AND_ASSIGN(ScrollViewWithBorder); }; @@ -116,6 +110,8 @@ ScrollView::ScrollView() horiz_sb_(new NativeScrollBar(true)), vert_sb_(new NativeScrollBar(false)), resize_corner_(NULL), + min_height_(-1), + max_height_(-1), hide_horizontal_scrollbar_(false) { set_notify_enter_exit_on_child(true); @@ -162,6 +158,11 @@ gfx::Rect ScrollView::GetVisibleRect() const { contents_viewport_->width(), contents_viewport_->height()); } +void ScrollView::ClipHeightTo(int min_height, int max_height) { + min_height_ = min_height; + max_height_ = max_height; +} + int ScrollView::GetScrollBarWidth() const { return vert_sb_ ? vert_sb_->GetLayoutSize() : 0; } @@ -186,7 +187,40 @@ void ScrollView::SetVerticalScrollBar(ScrollBar* vert_sb) { vert_sb_ = vert_sb; } +gfx::Size ScrollView::GetPreferredSize() const { + if (!is_bounded()) + return View::GetPreferredSize(); + + gfx::Size size = contents()->GetPreferredSize(); + size.SetToMax(gfx::Size(size.width(), min_height_)); + size.SetToMin(gfx::Size(size.width(), max_height_)); + gfx::Insets insets = GetInsets(); + size.Enlarge(insets.width(), insets.height()); + return size; +} + +int ScrollView::GetHeightForWidth(int width) const { + if (!is_bounded()) + return View::GetHeightForWidth(width); + + gfx::Insets insets = GetInsets(); + width = std::max(0, width - insets.width()); + int height = contents()->GetHeightForWidth(width) + insets.height(); + return std::min(std::max(height, min_height_), max_height_); +} + void ScrollView::Layout() { + if (is_bounded()) { + int content_width = width(); + int content_height = contents()->GetHeightForWidth(content_width); + if (content_height > height()) { + content_width = std::max(content_width - GetScrollBarWidth(), 0); + content_height = contents()->GetHeightForWidth(content_width); + } + if (contents()->bounds().size() != gfx::Size(content_width, content_height)) + contents()->SetBounds(0, 0, content_width, content_height); + } + // Most views will want to auto-fit the available space. Most of them want to // use all available width (without overflowing) and only overflow in // height. Examples are HistoryView, MostVisitedView, DownloadTabView, etc. diff --git a/chromium/ui/views/controls/scroll_view.h b/chromium/ui/views/controls/scroll_view.h index da87ffdc753..c1c18713af0 100644 --- a/chromium/ui/views/controls/scroll_view.h +++ b/chromium/ui/views/controls/scroll_view.h @@ -31,6 +31,7 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController { static const char kViewClassName[]; ScrollView(); + virtual ~ScrollView(); // Creates a ScrollView with a theme specific border. @@ -52,6 +53,13 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController { hide_horizontal_scrollbar_ = visible; } + // Turns this scroll view into a bounded scroll view, with a fixed height. + // By default, a ScrollView will stretch to fill its outer container. + void ClipHeightTo(int min_height, int max_height); + + // Returns whether or not the ScrollView is bounded (as set by ClipHeightTo). + bool is_bounded() const { return max_height_ >= 0 && min_height_ >= 0; } + // Retrieves the width/height of scrollbars. These return 0 if the scrollbar // has not yet been created. int GetScrollBarWidth() const; @@ -67,6 +75,8 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController { void SetVerticalScrollBar(ScrollBar* vert_sb); // View overrides: + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual int GetHeightForWidth(int width) const OVERRIDE; virtual void Layout() OVERRIDE; virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE; virtual bool OnMouseWheel(const ui::MouseWheelEvent& e) OVERRIDE; @@ -127,6 +137,11 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController { // Resize corner. View* resize_corner_; + // The min and max height for the bounded scroll view. These are negative + // values if the view is not bounded. + int min_height_; + int max_height_; + // If true, never show the horizontal scrollbar (even if the contents is wider // than the viewport). bool hide_horizontal_scrollbar_; diff --git a/chromium/ui/views/controls/scroll_view_unittest.cc b/chromium/ui/views/controls/scroll_view_unittest.cc index f0bdbd5443d..8f34377553f 100644 --- a/chromium/ui/views/controls/scroll_view_unittest.cc +++ b/chromium/ui/views/controls/scroll_view_unittest.cc @@ -5,11 +5,17 @@ #include "ui/views/controls/scroll_view.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/views/controls/scrollbar/overlay_scroll_bar.h" +#include "ui/views/test/test_views.h" namespace views { namespace { +const int kWidth = 100; +const int kMinHeight = 50; +const int kMaxHeight = 100; + // View implementation that allows setting the preferred size. class CustomView : public View { public: @@ -20,7 +26,7 @@ class CustomView : public View { PreferredSizeChanged(); } - virtual gfx::Size GetPreferredSize() OVERRIDE { + virtual gfx::Size GetPreferredSize() const OVERRIDE { return preferred_size_; } @@ -148,7 +154,6 @@ TEST(ScrollViewTest, ScrollBarsWithHeader) { ASSERT_TRUE(scroll_view.vertical_scroll_bar() != NULL); EXPECT_TRUE(scroll_view.vertical_scroll_bar()->visible()); - // Size the contents such that horizontal scrollbar is needed. contents->SetBounds(0, 0, 400, 50); scroll_view.Layout(); @@ -214,7 +219,6 @@ TEST(ScrollViewTest, HeaderScrollsWithContent) { EXPECT_EQ("-1,0", header->bounds().origin().ToString()); } - // Verifies ScrollRectToVisible() on the child works. TEST(ScrollViewTest, ScrollRectToVisible) { ScrollView scroll_view; @@ -237,4 +241,97 @@ TEST(ScrollViewTest, ScrollRectToVisible) { EXPECT_EQ(-(415 - viewport_height), contents->y()); } +// Verifies ClipHeightTo() uses the height of the content when it is between the +// minimum and maximum height values. +TEST(ScrollViewTest, ClipHeightToNormalContentHeight) { + ScrollView scroll_view; + + scroll_view.ClipHeightTo(kMinHeight, kMaxHeight); + + const int kNormalContentHeight = 75; + scroll_view.SetContents( + new views::StaticSizedView(gfx::Size(kWidth, kNormalContentHeight))); + + EXPECT_EQ(gfx::Size(kWidth, kNormalContentHeight), + scroll_view.GetPreferredSize()); + + scroll_view.SizeToPreferredSize(); + scroll_view.Layout(); + + EXPECT_EQ(gfx::Size(kWidth, kNormalContentHeight), + scroll_view.contents()->size()); + EXPECT_EQ(gfx::Size(kWidth, kNormalContentHeight), scroll_view.size()); +} + +// Verifies ClipHeightTo() uses the minimum height when the content is shorter +// thamn the minimum height value. +TEST(ScrollViewTest, ClipHeightToShortContentHeight) { + ScrollView scroll_view; + + scroll_view.ClipHeightTo(kMinHeight, kMaxHeight); + + const int kShortContentHeight = 10; + scroll_view.SetContents( + new views::StaticSizedView(gfx::Size(kWidth, kShortContentHeight))); + + EXPECT_EQ(gfx::Size(kWidth, kMinHeight), scroll_view.GetPreferredSize()); + + scroll_view.SizeToPreferredSize(); + scroll_view.Layout(); + + EXPECT_EQ(gfx::Size(kWidth, kShortContentHeight), + scroll_view.contents()->size()); + EXPECT_EQ(gfx::Size(kWidth, kMinHeight), scroll_view.size()); +} + +// Verifies ClipHeightTo() uses the maximum height when the content is longer +// thamn the maximum height value. +TEST(ScrollViewTest, ClipHeightToTallContentHeight) { + ScrollView scroll_view; + + // Use a scrollbar that is disabled by default, so the width of the content is + // not affected. + scroll_view.SetVerticalScrollBar(new views::OverlayScrollBar(false)); + + scroll_view.ClipHeightTo(kMinHeight, kMaxHeight); + + const int kTallContentHeight = 1000; + scroll_view.SetContents( + new views::StaticSizedView(gfx::Size(kWidth, kTallContentHeight))); + + EXPECT_EQ(gfx::Size(kWidth, kMaxHeight), scroll_view.GetPreferredSize()); + + scroll_view.SizeToPreferredSize(); + scroll_view.Layout(); + + EXPECT_EQ(gfx::Size(kWidth, kTallContentHeight), + scroll_view.contents()->size()); + EXPECT_EQ(gfx::Size(kWidth, kMaxHeight), scroll_view.size()); +} + +// Verifies that when ClipHeightTo() produces a scrollbar, it reduces the width +// of the inner content of the ScrollView. +TEST(ScrollViewTest, ClipHeightToScrollbarUsesWidth) { + ScrollView scroll_view; + + scroll_view.ClipHeightTo(kMinHeight, kMaxHeight); + + // Create a view that will be much taller than it is wide. + scroll_view.SetContents(new views::ProportionallySizedView(1000)); + + // Without any width, it will default to 0,0 but be overridden by min height. + scroll_view.SizeToPreferredSize(); + EXPECT_EQ(gfx::Size(0, kMinHeight), scroll_view.GetPreferredSize()); + + gfx::Size new_size(kWidth, scroll_view.GetHeightForWidth(kWidth)); + scroll_view.SetSize(new_size); + scroll_view.Layout(); + + int scroll_bar_width = scroll_view.GetScrollBarWidth(); + int expected_width = kWidth - scroll_bar_width; + EXPECT_EQ(scroll_view.contents()->size().width(), expected_width); + EXPECT_EQ(scroll_view.contents()->size().height(), 1000 * expected_width); + EXPECT_EQ(gfx::Size(kWidth, kMaxHeight), scroll_view.size()); +} + } // namespace views diff --git a/chromium/ui/views/controls/scrollbar/base_scroll_bar.cc b/chromium/ui/views/controls/scrollbar/base_scroll_bar.cc index 6271c2e1bfe..5e4d6a56fa3 100644 --- a/chromium/ui/views/controls/scrollbar/base_scroll_bar.cc +++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar.cc @@ -17,6 +17,7 @@ #include "ui/events/event.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/canvas.h" +#include "ui/gfx/geometry/safe_integer_conversions.h" #include "ui/views/controls/menu/menu_item_view.h" #include "ui/views/controls/menu/menu_runner.h" #include "ui/views/controls/scroll_view.h" @@ -235,10 +236,19 @@ void BaseScrollBar::OnGestureEvent(ui::GestureEvent* event) { } if (event->type() == ui::ET_GESTURE_SCROLL_UPDATE) { - if (ScrollByContentsOffset(IsHorizontal() ? event->details().scroll_x() : - event->details().scroll_y())) { - event->SetHandled(); + float scroll_amount_f; + int scroll_amount; + if (IsHorizontal()) { + scroll_amount_f = event->details().scroll_x() - roundoff_error_.x(); + scroll_amount = gfx::ToRoundedInt(scroll_amount_f); + roundoff_error_.set_x(scroll_amount - scroll_amount_f); + } else { + scroll_amount_f = event->details().scroll_y() - roundoff_error_.y(); + scroll_amount = gfx::ToRoundedInt(scroll_amount_f); + roundoff_error_.set_y(scroll_amount - scroll_amount_f); } + if (ScrollByContentsOffset(scroll_amount)) + event->SetHandled(); return; } @@ -295,17 +305,22 @@ void BaseScrollBar::ShowContextMenuForView(View* source, menu->AppendSeparator(); menu->AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollPrev); menu->AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollNext); - if (menu_runner_->RunMenuAt(GetWidget(), NULL, gfx::Rect(p, gfx::Size()), - views::MenuItemView::TOPLEFT, source_type, MenuRunner::HAS_MNEMONICS | - views::MenuRunner::CONTEXT_MENU) == - MenuRunner::MENU_DELETED) + if (menu_runner_->RunMenuAt( + GetWidget(), + NULL, + gfx::Rect(p, gfx::Size()), + MENU_ANCHOR_TOPLEFT, + source_type, + MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU) == + MenuRunner::MENU_DELETED) { return; + } } /////////////////////////////////////////////////////////////////////////////// // BaseScrollBar, Menu::Delegate implementation: -string16 BaseScrollBar::GetLabel(int id) const { +base::string16 BaseScrollBar::GetLabel(int id) const { int ids_value = 0; switch (id) { case ScrollBarContextMenuCommand_ScrollHere: @@ -337,7 +352,7 @@ string16 BaseScrollBar::GetLabel(int id) const { NOTREACHED() << "Invalid BaseScrollBar Context Menu command!"; } - return ids_value ? l10n_util::GetStringUTF16(ids_value) : string16(); + return ids_value ? l10n_util::GetStringUTF16(ids_value) : base::string16(); } bool BaseScrollBar::IsCommandEnabled(int id) const { @@ -378,7 +393,8 @@ void BaseScrollBar::ExecuteCommand(int id) { /////////////////////////////////////////////////////////////////////////////// // BaseScrollBar, ScrollBar implementation: -void BaseScrollBar::Update(int viewport_size, int content_size, +void BaseScrollBar::Update(int viewport_size, + int content_size, int contents_scroll_offset) { ScrollBar::Update(viewport_size, content_size, contents_scroll_offset); @@ -394,6 +410,7 @@ void BaseScrollBar::Update(int viewport_size, int content_size, contents_scroll_offset = 0; if (contents_scroll_offset > content_size) contents_scroll_offset = content_size; + contents_scroll_offset_ = contents_scroll_offset; // Thumb Height and Thumb Pos. // The height of the thumb is the ratio of the Viewport height to the @@ -488,7 +505,7 @@ int BaseScrollBar::CalculateThumbPosition(int contents_scroll_offset) const { } int BaseScrollBar::CalculateContentsOffset(int thumb_position, - bool scroll_to_middle) const { + bool scroll_to_middle) const { if (scroll_to_middle) thumb_position = thumb_position - (thumb_->GetSize() / 2); return (thumb_position * contents_size_) / GetTrackSize(); diff --git a/chromium/ui/views/controls/scrollbar/base_scroll_bar.h b/chromium/ui/views/controls/scrollbar/base_scroll_bar.h index a517b6c0645..068ad4ddb6f 100644 --- a/chromium/ui/views/controls/scrollbar/base_scroll_bar.h +++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar.h @@ -64,7 +64,7 @@ class VIEWS_EXPORT BaseScrollBar : public ScrollBar, CustomButton::ButtonState new_state); // View overrides: - virtual gfx::Size GetPreferredSize() OVERRIDE = 0; + virtual gfx::Size GetPreferredSize() const OVERRIDE = 0; virtual void Layout() OVERRIDE = 0; virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE; virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE; @@ -93,7 +93,7 @@ class VIEWS_EXPORT BaseScrollBar : public ScrollBar, ui::MenuSourceType source_type) OVERRIDE; // Menu::Delegate overrides: - virtual string16 GetLabel(int id) const OVERRIDE; + virtual base::string16 GetLabel(int id) const OVERRIDE; virtual bool IsCommandEnabled(int id) const OVERRIDE; virtual void ExecuteCommand(int id) OVERRIDE; @@ -178,6 +178,12 @@ class VIEWS_EXPORT BaseScrollBar : public ScrollBar, scoped_ptr<MenuRunner> menu_runner_; scoped_ptr<ScrollAnimator> scroll_animator_; + // Difference between current position and cumulative deltas obtained from + // scroll update events. + // TODO(tdresser): This should be removed when raw pixel scrolling for views + // is enabled. See crbug.com/329354. + gfx::Vector2dF roundoff_error_; + DISALLOW_COPY_AND_ASSIGN(BaseScrollBar); }; diff --git a/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.cc b/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.cc index 6eba692d626..0a765c42962 100644 --- a/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.cc +++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.cc @@ -35,15 +35,12 @@ void BaseScrollBarButton::OnMouseCaptureLost() { void BaseScrollBarButton::RepeaterNotifyClick() { // TODO(sky): See if we can convert to using |Screen| everywhere. -#if defined(OS_WIN) && !defined(USE_AURA) - gfx::Point cursor_point(GetMessagePos()); -#else // TODO(scottmg): Native is wrong: http://crbug.com/133312 gfx::Point cursor_point = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint(); -#endif ui::MouseEvent event(ui::ET_MOUSE_RELEASED, cursor_point, cursor_point, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); Button::NotifyClick(event); } diff --git a/chromium/ui/views/controls/scrollbar/base_scroll_bar_thumb.h b/chromium/ui/views/controls/scrollbar/base_scroll_bar_thumb.h index 0859fa903c1..c755441b8b4 100644 --- a/chromium/ui/views/controls/scrollbar/base_scroll_bar_thumb.h +++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar_thumb.h @@ -44,7 +44,7 @@ class VIEWS_EXPORT BaseScrollBarThumb : public View { int GetPosition() const; // View overrides: - virtual gfx::Size GetPreferredSize() OVERRIDE = 0; + virtual gfx::Size GetPreferredSize() const OVERRIDE = 0; protected: // View overrides: diff --git a/chromium/ui/views/controls/scrollbar/bitmap_scroll_bar.cc b/chromium/ui/views/controls/scrollbar/bitmap_scroll_bar.cc deleted file mode 100644 index 2597cee0b78..00000000000 --- a/chromium/ui/views/controls/scrollbar/bitmap_scroll_bar.cc +++ /dev/null @@ -1,309 +0,0 @@ -// 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/controls/scrollbar/bitmap_scroll_bar.h" - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/callback.h" -#include "base/compiler_specific.h" -#include "base/message_loop/message_loop.h" -#include "base/strings/string16.h" -#include "base/strings/utf_string_conversions.h" -#include "build/build_config.h" -#include "grit/ui_strings.h" -#include "ui/base/l10n/l10n_util.h" -#include "ui/events/keycodes/keyboard_codes.h" -#include "ui/gfx/canvas.h" -#include "ui/gfx/image/image_skia.h" -#include "ui/views/controls/menu/menu.h" -#include "ui/views/controls/scroll_view.h" -#include "ui/views/controls/scrollbar/base_scroll_bar_thumb.h" -#include "ui/views/widget/widget.h" - -#if defined(OS_LINUX) -#include "ui/views/screen.h" -#endif - -#undef min -#undef max - -namespace views { - -namespace { - -// The distance the mouse can be dragged outside the bounds of the thumb during -// dragging before the scrollbar will snap back to its regular position. -const int kScrollThumbDragOutSnap = 100; - -/////////////////////////////////////////////////////////////////////////////// -// -// AutorepeatButton -// -// A button that activates on mouse pressed rather than released, and that -// continues to fire the clicked action as the mouse button remains pressed -// down on the button. -// -/////////////////////////////////////////////////////////////////////////////// -class AutorepeatButton : public ImageButton { - public: - explicit AutorepeatButton(ButtonListener* listener) - : ImageButton(listener), - repeater_(base::Bind(&AutorepeatButton::NotifyClick, - base::Unretained(this))) { - } - virtual ~AutorepeatButton() {} - - protected: - virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE { - Button::NotifyClick(event); - repeater_.Start(); - return true; - } - - virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE { - OnMouseCaptureLost(); - } - - virtual void OnMouseCaptureLost() OVERRIDE { - repeater_.Stop(); - } - - private: - void NotifyClick() { -#if defined(OS_WIN) - gfx::Point cursor_point(GetMessagePos()); -#elif defined(OS_LINUX) - gfx::Point cursor_point = gfx::Screen::GetCursorScreenPoint(); -#endif - ui::MouseEvent event(ui::ET_MOUSE_RELEASED, - cursor_point, cursor_point, - ui::EF_LEFT_MOUSE_BUTTON); - Button::NotifyClick(event); - } - - // The repeat controller that we use to repeatedly click the button when the - // mouse button is down. - RepeatController repeater_; - - DISALLOW_COPY_AND_ASSIGN(AutorepeatButton); -}; - -/////////////////////////////////////////////////////////////////////////////// -// -// BitmapScrollBarThumb -// -// A view that acts as the thumb in the scroll bar track that the user can -// drag to scroll the associated contents view within the viewport. -// -/////////////////////////////////////////////////////////////////////////////// -class BitmapScrollBarThumb : public BaseScrollBarThumb { - public: - explicit BitmapScrollBarThumb(BitmapScrollBar* scroll_bar) - : BaseScrollBarThumb(scroll_bar), - scroll_bar_(scroll_bar) { - } - virtual ~BitmapScrollBarThumb() { } - - // View overrides: - virtual gfx::Size GetPreferredSize() OVERRIDE { - return gfx::Size(background_image()->width(), - start_cap_image()->height() + - end_cap_image()->height() + - grippy_image()->height()); - } - - protected: - // View overrides: - virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { - canvas->DrawImageInt(*start_cap_image(), 0, 0); - int top_cap_height = start_cap_image()->height(); - int bottom_cap_height = end_cap_image()->height(); - int thumb_body_height = height() - top_cap_height - bottom_cap_height; - canvas->TileImageInt(*background_image(), 0, top_cap_height, - background_image()->width(), thumb_body_height); - canvas->DrawImageInt(*end_cap_image(), 0, - height() - bottom_cap_height); - - // Paint the grippy over the track. - int grippy_x = (width() - grippy_image()->width()) / 2; - int grippy_y = (thumb_body_height - grippy_image()->height()) / 2; - canvas->DrawImageInt(*grippy_image(), grippy_x, grippy_y); - } - - private: - // Returns the image rendered at the start of the thumb. - gfx::ImageSkia* start_cap_image() const { - return scroll_bar_->images_[BitmapScrollBar::THUMB_START_CAP][GetState()]; - } - - // Returns the image rendered at the end of the thumb. - gfx::ImageSkia* end_cap_image() const { - return scroll_bar_->images_[BitmapScrollBar::THUMB_END_CAP][GetState()]; - } - - // Returns the image that is tiled in the background of the thumb between - // the start and the end caps. - gfx::ImageSkia* background_image() const { - return scroll_bar_->images_[BitmapScrollBar::THUMB_MIDDLE][GetState()]; - } - - // Returns the image that is rendered in the middle of the thumb - // transparently over the background image. - gfx::ImageSkia* grippy_image() const { - return scroll_bar_->images_[BitmapScrollBar::THUMB_GRIPPY] - [CustomButton::STATE_NORMAL]; - } - - // The BitmapScrollBar that owns us. - BitmapScrollBar* scroll_bar_; - - DISALLOW_COPY_AND_ASSIGN(BitmapScrollBarThumb); -}; - -} // namespace - -/////////////////////////////////////////////////////////////////////////////// -// BitmapScrollBar, public: - -BitmapScrollBar::BitmapScrollBar(bool horizontal, bool show_scroll_buttons) - : BaseScrollBar(horizontal, new BitmapScrollBarThumb(this)), - prev_button_(new AutorepeatButton(this)), - next_button_(new AutorepeatButton(this)), - show_scroll_buttons_(show_scroll_buttons) { - if (!show_scroll_buttons_) { - prev_button_->SetVisible(false); - next_button_->SetVisible(false); - } - - AddChildView(prev_button_); - AddChildView(next_button_); - - set_context_menu_controller(this); - prev_button_->set_context_menu_controller(this); - next_button_->set_context_menu_controller(this); -} - -void BitmapScrollBar::SetImage(ScrollBarPart part, - CustomButton::ButtonState state, - gfx::ImageSkia* image_skia) { - DCHECK(part < PART_COUNT); - DCHECK(state < CustomButton::STATE_COUNT); - switch (part) { - case PREV_BUTTON: - prev_button_->SetImage(state, image_skia); - break; - case NEXT_BUTTON: - next_button_->SetImage(state, image_skia); - break; - case THUMB_START_CAP: - case THUMB_MIDDLE: - case THUMB_END_CAP: - case THUMB_GRIPPY: - case THUMB_TRACK: - images_[part][state] = image_skia; - break; - } -} - -int BitmapScrollBar::GetLayoutSize() const { - gfx::Size prefsize = prev_button_->GetPreferredSize(); - return IsHorizontal() ? prefsize.height() : prefsize.width(); -} - -gfx::Rect BitmapScrollBar::GetTrackBounds() const { - gfx::Size prefsize = prev_button_->GetPreferredSize(); - if (IsHorizontal()) { - if (!show_scroll_buttons_) - prefsize.set_width(0); - int new_width = - std::max(0, width() - (prefsize.width() * 2)); - gfx::Rect track_bounds(prefsize.width(), 0, new_width, prefsize.height()); - return track_bounds; - } - if (!show_scroll_buttons_) - prefsize.set_height(0); - gfx::Rect track_bounds(0, prefsize.height(), prefsize.width(), - std::max(0, height() - (prefsize.height() * 2))); - return track_bounds; -} - -/////////////////////////////////////////////////////////////////////////////// -// BitmapScrollBar, View implementation: - -gfx::Size BitmapScrollBar::GetPreferredSize() { - // In this case, we're returning the desired width of the scrollbar and its - // minimum allowable height. - gfx::Size button_prefsize = prev_button_->GetPreferredSize(); - return gfx::Size(button_prefsize.width(), button_prefsize.height() * 2); -} - -void BitmapScrollBar::Layout() { - // Size and place the two scroll buttons. - if (show_scroll_buttons_) { - gfx::Size prefsize = prev_button_->GetPreferredSize(); - prev_button_->SetBounds(0, 0, prefsize.width(), prefsize.height()); - prefsize = next_button_->GetPreferredSize(); - if (IsHorizontal()) { - next_button_->SetBounds(width() - prefsize.width(), 0, prefsize.width(), - prefsize.height()); - } else { - next_button_->SetBounds(0, height() - prefsize.height(), prefsize.width(), - prefsize.height()); - } - } else { - prev_button_->SetBounds(0, 0, 0, 0); - next_button_->SetBounds(0, 0, 0, 0); - } - - BaseScrollBarThumb* thumb = GetThumb(); - // Size and place the thumb - gfx::Size thumb_prefsize = thumb->GetPreferredSize(); - gfx::Rect track_bounds = GetTrackBounds(); - - // Preserve the height/width of the thumb (depending on orientation) as set - // by the last call to |Update|, but coerce the width/height to be the - // appropriate value for the images provided. - if (IsHorizontal()) { - thumb->SetBounds(thumb->x(), thumb->y(), thumb->width(), - thumb_prefsize.height()); - } else { - thumb->SetBounds(thumb->x(), thumb->y(), thumb_prefsize.width(), - thumb->height()); - } - - // Hide the thumb if the track isn't tall enough to display even a tiny - // thumb. The user can only use the mousewheel, scroll buttons or keyboard - // in this scenario. - if ((IsHorizontal() && (track_bounds.width() < thumb_prefsize.width()) || - (!IsHorizontal() && (track_bounds.height() < thumb_prefsize.height())))) { - thumb->SetVisible(false); - } else if (!thumb->visible()) { - thumb->SetVisible(true); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// BitmapScrollBar, View implementation: - -void BitmapScrollBar::OnPaint(gfx::Canvas* canvas) { - // Paint the track. - gfx::Rect track_bounds = GetTrackBounds(); - canvas->TileImageInt(*images_[THUMB_TRACK][GetThumbTrackState()], - track_bounds.x(), track_bounds.y(), - track_bounds.width(), track_bounds.height()); -} - -/////////////////////////////////////////////////////////////////////////////// -// BitmapScrollBar, ButtonListener implementation: - -void BitmapScrollBar::ButtonPressed(Button* sender, const ui::Event& event) { - if (sender == prev_button_) { - ScrollByAmount(SCROLL_PREV_LINE); - } else if (sender == next_button_) { - ScrollByAmount(SCROLL_NEXT_LINE); - } -} - -} // namespace views diff --git a/chromium/ui/views/controls/scrollbar/bitmap_scroll_bar.h b/chromium/ui/views/controls/scrollbar/bitmap_scroll_bar.h deleted file mode 100644 index ac97af499cc..00000000000 --- a/chromium/ui/views/controls/scrollbar/bitmap_scroll_bar.h +++ /dev/null @@ -1,91 +0,0 @@ -// 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_CONTROLS_SCROLLBAR_BITMAP_SCROLL_BAR_H_ -#define UI_VIEWS_CONTROLS_SCROLLBAR_BITMAP_SCROLL_BAR_H_ - -#include "ui/views/controls/scrollbar/base_scroll_bar.h" - -namespace views { - -namespace { -class BitmapScrollBarThumb; -} - -/////////////////////////////////////////////////////////////////////////////// -// -// BitmapScrollBar -// -// A ScrollBar subclass that implements a scroll bar rendered using images -// that the user provides. There are images for the up and down buttons, as -// well as for the thumb and track. This is intended for creating UIs that -// have customized, non-native appearances, like floating HUDs etc. -// -/////////////////////////////////////////////////////////////////////////////// -class VIEWS_EXPORT BitmapScrollBar : public BaseScrollBar, - public ButtonListener { - public: - BitmapScrollBar(bool horizontal, bool show_scroll_buttons); - virtual ~BitmapScrollBar() { } - - // A list of parts that the user may supply images for. - enum ScrollBarPart { - // The button used to represent scrolling up/left by 1 line. - PREV_BUTTON = 0, - // The button used to represent scrolling down/right by 1 line. - // IMPORTANT: The code assumes the prev and next - // buttons have equal width and equal height. - NEXT_BUTTON, - // The top/left segment of the thumb on the scrollbar. - THUMB_START_CAP, - // The tiled background image of the thumb. - THUMB_MIDDLE, - // The bottom/right segment of the thumb on the scrollbar. - THUMB_END_CAP, - // The grippy that is rendered in the center of the thumb. - THUMB_GRIPPY, - // The tiled background image of the thumb track. - THUMB_TRACK, - PART_COUNT - }; - - // Sets the image to be rendered for the specified part and state. - void SetImage(ScrollBarPart part, - CustomButton::ButtonState state, - gfx::ImageSkia* image_skia); - - - gfx::Rect GetTrackBounds() const; - - protected: - // View overrides: - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual void Layout() OVERRIDE; - virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; - - // ScrollBar overrides: - virtual int GetLayoutSize() const OVERRIDE; - - // BaseButton::ButtonListener overrides: - virtual void ButtonPressed(Button* sender, - const ui::Event& event) OVERRIDE; - - private: - // Up/Down/Left/Right buttons. - ImageButton* prev_button_; - ImageButton* next_button_; - - // The thumb needs to be able to access the part images. - friend BitmapScrollBarThumb; - gfx::ImageSkia* images_[PART_COUNT][CustomButton::STATE_COUNT]; - - // True if the scroll buttons at each end of the scroll bar should be shown. - bool show_scroll_buttons_; - - DISALLOW_COPY_AND_ASSIGN(BitmapScrollBar); -}; - -} // namespace views - -#endif // UI_VIEWS_CONTROLS_SCROLLBAR_BITMAP_SCROLL_BAR_H_ diff --git a/chromium/ui/views/controls/scrollbar/kennedy_scroll_bar.cc b/chromium/ui/views/controls/scrollbar/kennedy_scroll_bar.cc index f263068ecd7..b3d1e6a3c32 100644 --- a/chromium/ui/views/controls/scrollbar/kennedy_scroll_bar.cc +++ b/chromium/ui/views/controls/scrollbar/kennedy_scroll_bar.cc @@ -28,7 +28,7 @@ class KennedyScrollBarThumb : public BaseScrollBarThumb { protected: // View overrides: - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; private: @@ -42,7 +42,7 @@ KennedyScrollBarThumb::KennedyScrollBarThumb(BaseScrollBar* scroll_bar) KennedyScrollBarThumb::~KennedyScrollBarThumb() { } -gfx::Size KennedyScrollBarThumb::GetPreferredSize() { +gfx::Size KennedyScrollBarThumb::GetPreferredSize() const { return gfx::Size(kThumbMinimumSize, kThumbMinimumSize); } @@ -77,7 +77,7 @@ int KennedyScrollBar::GetLayoutSize() const { return kScrollbarWidth; } -gfx::Size KennedyScrollBar::GetPreferredSize() { +gfx::Size KennedyScrollBar::GetPreferredSize() const { return GetTrackBounds().size(); } diff --git a/chromium/ui/views/controls/scrollbar/kennedy_scroll_bar.h b/chromium/ui/views/controls/scrollbar/kennedy_scroll_bar.h index 332948387a5..ace3fae31ec 100644 --- a/chromium/ui/views/controls/scrollbar/kennedy_scroll_bar.h +++ b/chromium/ui/views/controls/scrollbar/kennedy_scroll_bar.h @@ -25,7 +25,7 @@ class VIEWS_EXPORT KennedyScrollBar : public BaseScrollBar { virtual int GetLayoutSize() const OVERRIDE; // View overrides: - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual void Layout() OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; diff --git a/chromium/ui/views/controls/scrollbar/native_scroll_bar.cc b/chromium/ui/views/controls/scrollbar/native_scroll_bar.cc index 9a913709a4b..7b45dd6e052 100644 --- a/chromium/ui/views/controls/scrollbar/native_scroll_bar.cc +++ b/chromium/ui/views/controls/scrollbar/native_scroll_bar.cc @@ -9,13 +9,10 @@ #include "base/message_loop/message_loop.h" #include "ui/events/event.h" +#include "ui/views/controls/scrollbar/native_scroll_bar_views.h" #include "ui/views/controls/scrollbar/native_scroll_bar_wrapper.h" #include "ui/views/widget/widget.h" -#if defined(USE_AURA) -#include "ui/views/controls/scrollbar/native_scroll_bar_views.h" -#endif - namespace views { // static @@ -45,7 +42,7 @@ int NativeScrollBar::GetVerticalScrollBarWidth( //////////////////////////////////////////////////////////////////////////////// // NativeScrollBar, View overrides: -gfx::Size NativeScrollBar::GetPreferredSize() { +gfx::Size NativeScrollBar::GetPreferredSize() const { if (native_wrapper_) return native_wrapper_->GetView()->GetPreferredSize(); return gfx::Size(); diff --git a/chromium/ui/views/controls/scrollbar/native_scroll_bar.h b/chromium/ui/views/controls/scrollbar/native_scroll_bar.h index 70d5a83d37a..058784d55ac 100644 --- a/chromium/ui/views/controls/scrollbar/native_scroll_bar.h +++ b/chromium/ui/views/controls/scrollbar/native_scroll_bar.h @@ -40,7 +40,7 @@ class VIEWS_EXPORT NativeScrollBar : public ScrollBar { FRIEND_TEST_ALL_PREFIXES(NativeScrollBarTest, Scrolling); // Overridden from View. - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual void Layout() OVERRIDE; virtual void ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) OVERRIDE; diff --git a/chromium/ui/views/controls/scrollbar/native_scroll_bar_views.cc b/chromium/ui/views/controls/scrollbar/native_scroll_bar_views.cc index a8402e5c655..b4f83ccdf7b 100644 --- a/chromium/ui/views/controls/scrollbar/native_scroll_bar_views.cc +++ b/chromium/ui/views/controls/scrollbar/native_scroll_bar_views.cc @@ -32,7 +32,7 @@ class ScrollBarButton : public BaseScrollBarButton { ScrollBarButton(ButtonListener* listener, Type type); virtual ~ScrollBarButton(); - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual const char* GetClassName() const OVERRIDE { return "ScrollBarButton"; } @@ -54,7 +54,7 @@ class ScrollBarThumb : public BaseScrollBarThumb { explicit ScrollBarThumb(BaseScrollBar* scroll_bar); virtual ~ScrollBarThumb(); - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual const char* GetClassName() const OVERRIDE { return "ScrollBarThumb"; } @@ -83,7 +83,7 @@ ScrollBarButton::ScrollBarButton(ButtonListener* listener, Type type) ScrollBarButton::~ScrollBarButton() { } -gfx::Size ScrollBarButton::GetPreferredSize() { +gfx::Size ScrollBarButton::GetPreferredSize() const { return GetNativeTheme()->GetPartSize(GetNativeThemePart(), GetNativeThemeState(), GetNativeThemeParams()); @@ -164,7 +164,7 @@ ScrollBarThumb::ScrollBarThumb(BaseScrollBar* scroll_bar) ScrollBarThumb::~ScrollBarThumb() { } -gfx::Size ScrollBarThumb::GetPreferredSize() { +gfx::Size ScrollBarThumb::GetPreferredSize() const { return GetNativeTheme()->GetPartSize(GetNativeThemePart(), GetNativeThemeState(), GetNativeThemeParams()); @@ -292,7 +292,7 @@ void NativeScrollBarViews::OnPaint(gfx::Canvas* canvas) { GetNativeTheme()->Paint(canvas->sk_canvas(), part_, state_, bounds, params_); } -gfx::Size NativeScrollBarViews::GetPreferredSize() { +gfx::Size NativeScrollBarViews::GetPreferredSize() const { const ui::NativeTheme* theme = native_scroll_bar_->GetNativeTheme(); if (native_scroll_bar_->IsHorizontal()) return gfx::Size(0, GetHorizontalScrollBarHeight(theme)); diff --git a/chromium/ui/views/controls/scrollbar/native_scroll_bar_views.h b/chromium/ui/views/controls/scrollbar/native_scroll_bar_views.h index 72d1821cbbf..38dbe0b6f8e 100644 --- a/chromium/ui/views/controls/scrollbar/native_scroll_bar_views.h +++ b/chromium/ui/views/controls/scrollbar/native_scroll_bar_views.h @@ -36,7 +36,7 @@ class VIEWS_EXPORT NativeScrollBarViews : public BaseScrollBar, // View overrides: virtual void Layout() OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual const char* GetClassName() const OVERRIDE; // ScrollBar overrides: diff --git a/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.cc b/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.cc index 176f7b9554e..1396bd1ec99 100644 --- a/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.cc +++ b/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.cc @@ -30,7 +30,7 @@ class OverlayScrollBarThumb : public BaseScrollBarThumb, protected: // View overrides: - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; // gfx::AnimationDelegate overrides: @@ -44,18 +44,16 @@ class OverlayScrollBarThumb : public BaseScrollBarThumb, OverlayScrollBarThumb::OverlayScrollBarThumb(BaseScrollBar* scroll_bar) : BaseScrollBarThumb(scroll_bar), animation_opacity_(0.0) { - if (get_use_acceleration_when_possible()) { - // This is necessary, otherwise the thumb will be rendered below the views - // if those views paint to their own layers. - SetPaintToLayer(true); - SetFillsBoundsOpaquely(false); - } + // This is necessary, otherwise the thumb will be rendered below the views if + // those views paint to their own layers. + SetPaintToLayer(true); + SetFillsBoundsOpaquely(false); } OverlayScrollBarThumb::~OverlayScrollBarThumb() { } -gfx::Size OverlayScrollBarThumb::GetPreferredSize() { +gfx::Size OverlayScrollBarThumb::GetPreferredSize() const { return gfx::Size(kThumbMinimumSize, kThumbMinimumSize); } @@ -140,7 +138,7 @@ void OverlayScrollBar::OnGestureEvent(ui::GestureEvent* event) { BaseScrollBar::OnGestureEvent(event); } -gfx::Size OverlayScrollBar::GetPreferredSize() { +gfx::Size OverlayScrollBar::GetPreferredSize() const { return gfx::Size(); } diff --git a/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.h b/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.h index b061da08586..428a27715c4 100644 --- a/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.h +++ b/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.h @@ -28,7 +28,7 @@ class VIEWS_EXPORT OverlayScrollBar : public BaseScrollBar { virtual void OnMouseExitedScrollView(const ui::MouseEvent& event) OVERRIDE; // View overrides: - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual void Layout() OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; diff --git a/chromium/ui/views/controls/scrollbar/scroll_bar.cc b/chromium/ui/views/controls/scrollbar/scroll_bar.cc index 5374d32ceef..014cf3b53fa 100644 --- a/chromium/ui/views/controls/scrollbar/scroll_bar.cc +++ b/chromium/ui/views/controls/scrollbar/scroll_bar.cc @@ -4,15 +4,15 @@ #include "ui/views/controls/scrollbar/scroll_bar.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" namespace views { ScrollBar::~ScrollBar() { } -void ScrollBar::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_SCROLLBAR; +void ScrollBar::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_SCROLL_BAR; } bool ScrollBar::IsHorizontal() const { diff --git a/chromium/ui/views/controls/scrollbar/scroll_bar.h b/chromium/ui/views/controls/scrollbar/scroll_bar.h index aa3800eb614..c8a31c43016 100644 --- a/chromium/ui/views/controls/scrollbar/scroll_bar.h +++ b/chromium/ui/views/controls/scrollbar/scroll_bar.h @@ -60,7 +60,7 @@ class VIEWS_EXPORT ScrollBar : public View { virtual ~ScrollBar(); // Overridden from View: - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; // Returns whether this scrollbar is horizontal. bool IsHorizontal() const; diff --git a/chromium/ui/views/controls/scrollbar/scrollbar_unittest.cc b/chromium/ui/views/controls/scrollbar/scrollbar_unittest.cc index cdf256a6207..c3c96df9b03 100644 --- a/chromium/ui/views/controls/scrollbar/scrollbar_unittest.cc +++ b/chromium/ui/views/controls/scrollbar/scrollbar_unittest.cc @@ -156,4 +156,15 @@ TEST_F(NativeScrollBarTest, MAYBE_ScrollBarFitsToBottom) { scrollbar_->GetPosition()); } +TEST_F(NativeScrollBarTest, ScrollToEndAfterShrinkAndExpand) { + // Scroll to the end of the content. + scrollbar_->Update(100, 101, 0); + EXPECT_TRUE(scrollbar_->ScrollByContentsOffset(-1)); + // Shrink and then re-exapnd the content. + scrollbar_->Update(100, 100, 0); + scrollbar_->Update(100, 101, 0); + // Ensure the scrollbar allows scrolling to the end. + EXPECT_TRUE(scrollbar_->ScrollByContentsOffset(-1)); +} + } // namespace views diff --git a/chromium/ui/views/controls/separator.cc b/chromium/ui/views/controls/separator.cc index bbac3ede9de..a8fe6da7ef3 100644 --- a/chromium/ui/views/controls/separator.cc +++ b/chromium/ui/views/controls/separator.cc @@ -4,7 +4,7 @@ #include "ui/views/controls/separator.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/gfx/canvas.h" namespace views { @@ -28,17 +28,17 @@ Separator::~Separator() { //////////////////////////////////////////////////////////////////////////////// // Separator, View overrides: -gfx::Size Separator::GetPreferredSize() { +gfx::Size Separator::GetPreferredSize() const { if (orientation_ == HORIZONTAL) return gfx::Size(width(), kSeparatorHeight); return gfx::Size(kSeparatorHeight, height()); } -void Separator::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_SEPARATOR; +void Separator::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_SPLITTER; } -void Separator::Paint(gfx::Canvas* canvas) { +void Separator::Paint(gfx::Canvas* canvas, const views::CullSet& cull_set) { canvas->FillRect(bounds(), kDefaultColor); } diff --git a/chromium/ui/views/controls/separator.h b/chromium/ui/views/controls/separator.h index 53d10041864..6f45f8cec7f 100644 --- a/chromium/ui/views/controls/separator.h +++ b/chromium/ui/views/controls/separator.h @@ -28,9 +28,10 @@ class VIEWS_EXPORT Separator : public View { virtual ~Separator(); // Overridden from View: - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; - virtual void Paint(gfx::Canvas* canvas) OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; + virtual void Paint(gfx::Canvas* canvas, + const views::CullSet& cull_set) OVERRIDE; virtual const char* GetClassName() const OVERRIDE; private: diff --git a/chromium/ui/views/controls/single_split_view.cc b/chromium/ui/views/controls/single_split_view.cc index 85dab92fa5d..fe50141293c 100644 --- a/chromium/ui/views/controls/single_split_view.cc +++ b/chromium/ui/views/controls/single_split_view.cc @@ -5,14 +5,12 @@ #include "ui/views/controls/single_split_view.h" #include "skia/ext/skia_utils_win.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" +#include "ui/base/cursor/cursor.h" #include "ui/gfx/canvas.h" #include "ui/views/background.h" #include "ui/views/controls/single_split_view_listener.h" - -#if defined(USE_AURA) -#include "ui/base/cursor/cursor.h" -#endif +#include "ui/views/native_cursor.h" namespace views { @@ -62,16 +60,16 @@ const char* SingleSplitView::GetClassName() const { return kViewClassName; } -void SingleSplitView::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_GROUPING; +void SingleSplitView::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_GROUP; state->name = accessible_name_; } -gfx::Size SingleSplitView::GetPreferredSize() { +gfx::Size SingleSplitView::GetPreferredSize() const { int width = 0; int height = 0; for (int i = 0; i < 2 && i < child_count(); ++i) { - View* view = child_at(i); + const View* view = child_at(i); gfx::Size pref = view->GetPreferredSize(); if (is_horizontal_) { width += pref.width(); @@ -91,14 +89,8 @@ gfx::Size SingleSplitView::GetPreferredSize() { gfx::NativeCursor SingleSplitView::GetCursor(const ui::MouseEvent& event) { if (!IsPointInDivider(event.location())) return gfx::kNullCursor; -#if defined(USE_AURA) - return is_horizontal_ ? - ui::kCursorEastWestResize : ui::kCursorNorthSouthResize; -#elif defined(OS_WIN) - static HCURSOR we_resize_cursor = LoadCursor(NULL, IDC_SIZEWE); - static HCURSOR ns_resize_cursor = LoadCursor(NULL, IDC_SIZENS); - return is_horizontal_ ? we_resize_cursor : ns_resize_cursor; -#endif + return is_horizontal_ ? GetNativeEastWestResizeCursor() + : GetNativeNorthSouthResizeCursor(); } int SingleSplitView::GetDividerSize() const { @@ -148,7 +140,7 @@ void SingleSplitView::CalculateChildrenBounds( } } -void SingleSplitView::SetAccessibleName(const string16& name) { +void SingleSplitView::SetAccessibleName(const base::string16& name) { accessible_name_ = name; } diff --git a/chromium/ui/views/controls/single_split_view.h b/chromium/ui/views/controls/single_split_view.h index f4a336a6ef6..c03c8340e35 100644 --- a/chromium/ui/views/controls/single_split_view.h +++ b/chromium/ui/views/controls/single_split_view.h @@ -34,11 +34,11 @@ class VIEWS_EXPORT SingleSplitView : public View { virtual void Layout() OVERRIDE; virtual const char* GetClassName() const OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; // SingleSplitView's preferred size is the sum of the preferred widths // and the max of the heights. - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; // Overriden to return a resize cursor when over the divider. virtual gfx::NativeCursor GetCursor(const ui::MouseEvent& event) OVERRIDE; @@ -77,7 +77,7 @@ class VIEWS_EXPORT SingleSplitView : public View { gfx::Rect* leading_bounds, gfx::Rect* trailing_bounds) const; - void SetAccessibleName(const string16& name); + void SetAccessibleName(const base::string16& name); protected: // View overrides. @@ -137,7 +137,7 @@ class VIEWS_EXPORT SingleSplitView : public View { SingleSplitViewListener* listener_; // The accessible name of this view. - string16 accessible_name_; + base::string16 accessible_name_; DISALLOW_COPY_AND_ASSIGN(SingleSplitView); }; diff --git a/chromium/ui/views/controls/single_split_view_listener.h b/chromium/ui/views/controls/single_split_view_listener.h index 5c33c665214..0d589c9cfb8 100644 --- a/chromium/ui/views/controls/single_split_view_listener.h +++ b/chromium/ui/views/controls/single_split_view_listener.h @@ -5,13 +5,15 @@ #ifndef UI_VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_LISTENER_H_ #define UI_VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_LISTENER_H_ +#include "ui/views/views_export.h" + namespace views { class SingleSplitView; // An interface implemented by objects that want to be notified when the // splitter moves. -class SingleSplitViewListener { +class VIEWS_EXPORT SingleSplitViewListener { public: // Invoked when split handle is moved by the user. |sender|'s divider_offset // is already set to the new value, but Layout has not happened yet. diff --git a/chromium/ui/views/controls/single_split_view_unittest.cc b/chromium/ui/views/controls/single_split_view_unittest.cc index f82c1b5b090..cfea48ea4ab 100644 --- a/chromium/ui/views/controls/single_split_view_unittest.cc +++ b/chromium/ui/views/controls/single_split_view_unittest.cc @@ -2,15 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "ui/views/controls/single_split_view.h" + #include "base/logging.h" -#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/views/controls/single_split_view.h" #include "ui/views/controls/single_split_view_listener.h" -using ::testing::_; -using ::testing::Return; - namespace { static void VerifySplitViewLayout(const views::SingleSplitView& split) { @@ -44,9 +41,21 @@ static void VerifySplitViewLayout(const views::SingleSplitView& split) { } } -class MockObserver : public views::SingleSplitViewListener { +class SingleSplitViewListenerImpl : public views::SingleSplitViewListener { public: - MOCK_METHOD1(SplitHandleMoved, bool(views::SingleSplitView*)); + SingleSplitViewListenerImpl() : count_(0) {} + + virtual bool SplitHandleMoved(views::SingleSplitView* sender) OVERRIDE { + ++count_; + return false; + } + + int count() const { return count_; } + + private: + int count_; + + DISALLOW_COPY_AND_ASSIGN(SingleSplitViewListenerImpl); }; class MinimumSizedView: public views::View { @@ -55,10 +64,10 @@ class MinimumSizedView: public views::View { private: gfx::Size min_size_; - virtual gfx::Size GetMinimumSize() OVERRIDE; + virtual gfx::Size GetMinimumSize() const OVERRIDE; }; -gfx::Size MinimumSizedView::GetMinimumSize() { +gfx::Size MinimumSizedView::GetMinimumSize() const { return min_size_; } @@ -141,20 +150,14 @@ TEST(SingleSplitViewTest, Resize) { } TEST(SingleSplitViewTest, MouseDrag) { - MockObserver observer; const int kMinimumChildSize = 25; MinimumSizedView *child0 = new MinimumSizedView(gfx::Size(5, kMinimumChildSize)); MinimumSizedView *child1 = new MinimumSizedView(gfx::Size(5, kMinimumChildSize)); + SingleSplitViewListenerImpl listener; SingleSplitView split( - child0, child1, SingleSplitView::VERTICAL_SPLIT, &observer); - - ON_CALL(observer, SplitHandleMoved(_)) - .WillByDefault(Return(true)); - // SplitHandleMoved is called for two mouse moves and one mouse capture loss. - EXPECT_CALL(observer, SplitHandleMoved(_)) - .Times(5); + child0, child1, SingleSplitView::VERTICAL_SPLIT, &listener); const int kTotalSplitSize = 100; split.SetBounds(0, 0, 10, kTotalSplitSize); @@ -166,49 +169,53 @@ TEST(SingleSplitViewTest, MouseDrag) { gfx::Point press_point(7, kInitialDividerOffset + kMouseOffset); ui::MouseEvent mouse_pressed( - ui::ET_MOUSE_PRESSED, press_point, press_point, 0); + ui::ET_MOUSE_PRESSED, press_point, press_point, 0, 0); ASSERT_TRUE(split.OnMousePressed(mouse_pressed)); EXPECT_EQ(kInitialDividerOffset, split.divider_offset()); + EXPECT_EQ(0, listener.count()); // Drag divider to the bottom. gfx::Point drag_1_point( 5, kInitialDividerOffset + kMouseOffset + kMouseMoveDelta); ui::MouseEvent mouse_dragged_1( - ui::ET_MOUSE_DRAGGED, drag_1_point, drag_1_point, 0); + ui::ET_MOUSE_DRAGGED, drag_1_point, drag_1_point, 0, 0); ASSERT_TRUE(split.OnMouseDragged(mouse_dragged_1)); EXPECT_EQ(kInitialDividerOffset + kMouseMoveDelta, split.divider_offset()); + EXPECT_EQ(1, listener.count()); // Drag divider to the top, beyond first child minimum size. gfx::Point drag_2_point( 7, kMinimumChildSize - 5); ui::MouseEvent mouse_dragged_2( - ui::ET_MOUSE_DRAGGED, drag_2_point, drag_2_point, 0); + ui::ET_MOUSE_DRAGGED, drag_2_point, drag_2_point, 0,0 ); ASSERT_TRUE(split.OnMouseDragged(mouse_dragged_2)); - EXPECT_EQ(kMinimumChildSize, - split.divider_offset()); + EXPECT_EQ(kMinimumChildSize, split.divider_offset()); + EXPECT_EQ(2, listener.count()); // Drag divider to the bottom, beyond second child minimum size. gfx::Point drag_3_point( 7, kTotalSplitSize - kMinimumChildSize + 5); ui::MouseEvent mouse_dragged_3( - ui::ET_MOUSE_DRAGGED, drag_3_point, drag_3_point, 0); + ui::ET_MOUSE_DRAGGED, drag_3_point, drag_3_point, 0, 0); ASSERT_TRUE(split.OnMouseDragged(mouse_dragged_3)); EXPECT_EQ(kTotalSplitSize - kMinimumChildSize - split.GetDividerSize(), split.divider_offset()); + EXPECT_EQ(3, listener.count()); // Drag divider between childs' minimum sizes. gfx::Point drag_4_point( 6, kInitialDividerOffset + kMouseOffset + kMouseMoveDelta * 2); ui::MouseEvent mouse_dragged_4( - ui::ET_MOUSE_DRAGGED, drag_4_point, drag_4_point, 0); + ui::ET_MOUSE_DRAGGED, drag_4_point, drag_4_point, 0, 0); ASSERT_TRUE(split.OnMouseDragged(mouse_dragged_4)); EXPECT_EQ(kInitialDividerOffset + kMouseMoveDelta * 2, split.divider_offset()); + EXPECT_EQ(4, listener.count()); gfx::Point release_point( 7, kInitialDividerOffset + kMouseOffset + kMouseMoveDelta * 2); ui::MouseEvent mouse_released( - ui::ET_MOUSE_RELEASED, release_point, release_point, 0); + ui::ET_MOUSE_RELEASED, release_point, release_point, 0, 0); split.OnMouseReleased(mouse_released); EXPECT_EQ(kInitialDividerOffset + kMouseMoveDelta * 2, split.divider_offset()); @@ -217,6 +224,7 @@ TEST(SingleSplitViewTest, MouseDrag) { // This shouldn't occur after mouse release, but it's sufficient for testing. split.OnMouseCaptureLost(); EXPECT_EQ(kInitialDividerOffset, split.divider_offset()); + EXPECT_EQ(5, listener.count()); } } // namespace views diff --git a/chromium/ui/views/controls/slide_out_view.cc b/chromium/ui/views/controls/slide_out_view.cc index d7cb3f6ef5f..d3cafecf7eb 100644 --- a/chromium/ui/views/controls/slide_out_view.cc +++ b/chromium/ui/views/controls/slide_out_view.cc @@ -14,10 +14,8 @@ SlideOutView::SlideOutView() : gesture_scroll_amount_(0.f) { // If accelerated compositing is not available, this widget tracks the // OnSlideOut event but does not render any visible changes. - if (get_use_acceleration_when_possible()) { - SetPaintToLayer(true); - SetFillsBoundsOpaquely(false); - } + SetPaintToLayer(true); + SetFillsBoundsOpaquely(false); } SlideOutView::~SlideOutView() { @@ -47,13 +45,11 @@ void SlideOutView::OnGestureEvent(ui::GestureEvent* event) { // The scroll-update events include the incremental scroll amount. gesture_scroll_amount_ += event->details().scroll_x(); - if (get_use_acceleration_when_possible()) { - gfx::Transform transform; - transform.Translate(gesture_scroll_amount_, 0.0); - layer()->SetTransform(transform); - layer()->SetOpacity( - 1.f - std::min(fabsf(gesture_scroll_amount_) / width(), 1.f)); - } + gfx::Transform transform; + transform.Translate(gesture_scroll_amount_, 0.0); + layer()->SetTransform(transform); + layer()->SetOpacity( + 1.f - std::min(fabsf(gesture_scroll_amount_) / width(), 1.f)); } else if (event->type() == ui::ET_GESTURE_SCROLL_END) { const float kScrollRatioForClosingNotification = 0.5f; @@ -70,9 +66,6 @@ void SlideOutView::OnGestureEvent(ui::GestureEvent* event) { } void SlideOutView::RestoreVisualState() { - if (!get_use_acceleration_when_possible()) - return; - // Restore the layer state. const int kSwipeRestoreDurationMS = 150; ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator()); @@ -83,11 +76,6 @@ void SlideOutView::RestoreVisualState() { } void SlideOutView::SlideOutAndClose(SlideDirection direction) { - if (!get_use_acceleration_when_possible()) { - // No animations, so don't wait to fire the OnSlideOut event. - OnSlideOut(); - return; - } const int kSwipeOutTotalDurationMS = 150; int swipe_out_duration = kSwipeOutTotalDurationMS * layer()->opacity(); ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator()); diff --git a/chromium/ui/views/controls/slider.cc b/chromium/ui/views/controls/slider.cc index 4be6f5a2d56..74e803d454b 100644 --- a/chromium/ui/views/controls/slider.cc +++ b/chromium/ui/views/controls/slider.cc @@ -4,6 +4,8 @@ #include "ui/views/controls/slider.h" +#include <algorithm> + #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/strings/stringprintf.h" @@ -12,7 +14,7 @@ #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkPaint.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/base/resource/resource_bundle.h" #include "ui/events/event.h" #include "ui/gfx/animation/slide_animation.h" @@ -45,7 +47,7 @@ enum BorderElements { CENTER_RIGHT, RIGHT, }; -} +} // namespace namespace views { @@ -81,7 +83,7 @@ void Slider::SetValueInternal(float value, SliderChangeReason reason) { value_is_valid_ = true; if (value < 0.0) - value = 0.0; + value = 0.0; else if (value > 1.0) value = 1.0; if (value_ == value) @@ -104,7 +106,7 @@ void Slider::SetValueInternal(float value, SliderChangeReason reason) { } if (accessibility_events_enabled_ && GetWidget()) { NotifyAccessibilityEvent( - ui::AccessibilityTypes::EVENT_VALUE_CHANGED, true); + ui::AX_EVENT_VALUE_CHANGED, true); } } @@ -167,7 +169,7 @@ void Slider::UpdateState(bool control_on) { SchedulePaint(); } -void Slider::SetAccessibleName(const string16& name) { +void Slider::SetAccessibleName(const base::string16& name) { accessible_name_ = name; } @@ -184,7 +186,7 @@ void Slider::OnPaintFocus(gfx::Canvas* canvas) { } } -gfx::Size Slider::GetPreferredSize() { +gfx::Size Slider::GetPreferredSize() const { const int kSizeMajor = 200; const int kSizeMinor = 40; @@ -267,8 +269,7 @@ void Slider::OnPaint(gfx::Canvas* canvas) { bool Slider::OnMousePressed(const ui::MouseEvent& event) { if (!event.IsOnlyLeftMouseButton()) return false; - if (listener_) - listener_->SliderDragStarted(this); + OnSliderDragStarted(); PrepareForMove(event.location()); MoveButtonTo(event.location()); return true; @@ -280,8 +281,7 @@ bool Slider::OnMouseDragged(const ui::MouseEvent& event) { } void Slider::OnMouseReleased(const ui::MouseEvent& event) { - if (listener_) - listener_->SliderDragEnded(this); + OnSliderDragEnded(); } bool Slider::OnKeyPressed(const ui::KeyEvent& event) { @@ -305,16 +305,37 @@ bool Slider::OnKeyPressed(const ui::KeyEvent& event) { return false; } +void Slider::OnFocus() { + View::OnFocus(); + SchedulePaint(); +} + +void Slider::OnBlur() { + View::OnBlur(); + SchedulePaint(); +} + void Slider::OnGestureEvent(ui::GestureEvent* event) { - if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN || - event->type() == ui::ET_GESTURE_TAP_DOWN) { - PrepareForMove(event->location()); - MoveButtonTo(event->location()); - event->SetHandled(); - } else if (event->type() == ui::ET_GESTURE_SCROLL_UPDATE || - event->type() == ui::ET_GESTURE_SCROLL_END) { - MoveButtonTo(event->location()); - event->SetHandled(); + switch (event->type()) { + // In a multi point gesture only the touch point will generate + // an ET_GESTURE_TAP_DOWN event. + case ui::ET_GESTURE_TAP_DOWN: + OnSliderDragStarted(); + PrepareForMove(event->location()); + // Intentional fall through to next case. + case ui::ET_GESTURE_SCROLL_BEGIN: + case ui::ET_GESTURE_SCROLL_UPDATE: + MoveButtonTo(event->location()); + event->SetHandled(); + break; + case ui::ET_GESTURE_END: + MoveButtonTo(event->location()); + event->SetHandled(); + if (event->details().touch_points() <= 1) + OnSliderDragEnded(); + break; + default: + break; } } @@ -323,11 +344,21 @@ void Slider::AnimationProgressed(const gfx::Animation* animation) { SchedulePaint(); } -void Slider::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_SLIDER; +void Slider::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_SLIDER; state->name = accessible_name_; - state->value = UTF8ToUTF16( - base::StringPrintf("%d%%", (int)(value_ * 100 + 0.5))); + state->value = base::UTF8ToUTF16( + base::StringPrintf("%d%%", static_cast<int>(value_ * 100 + 0.5))); +} + +void Slider::OnSliderDragStarted() { + if (listener_) + listener_->SliderDragStarted(this); +} + +void Slider::OnSliderDragEnded() { + if (listener_) + listener_->SliderDragEnded(this); } } // namespace views diff --git a/chromium/ui/views/controls/slider.h b/chromium/ui/views/controls/slider.h index cec24c2755b..33a760bfd66 100644 --- a/chromium/ui/views/controls/slider.h +++ b/chromium/ui/views/controls/slider.h @@ -18,6 +18,10 @@ class SlideAnimation; namespace views { +namespace test { +class SliderTestApi; +} + class Slider; enum SliderChangeReason { @@ -57,7 +61,7 @@ class VIEWS_EXPORT Slider : public View, public gfx::AnimationDelegate { // Set the delta used for changing the value via keyboard. void SetKeyboardIncrement(float increment); - void SetAccessibleName(const string16& name); + void SetAccessibleName(const base::string16& name); void set_enable_accessibility_events(bool enabled) { accessibility_events_enabled_ = enabled; @@ -69,6 +73,8 @@ class VIEWS_EXPORT Slider : public View, public gfx::AnimationDelegate { void UpdateState(bool control_on); private: + friend class test::SliderTestApi; + void SetValueInternal(float value, SliderChangeReason reason); // Should be called on the Mouse Down event. Used to calculate relative @@ -81,14 +87,22 @@ class VIEWS_EXPORT Slider : public View, public gfx::AnimationDelegate { void OnPaintFocus(gfx::Canvas* canvas); + // Notify the listener_, if not NULL, that dragging started. + void OnSliderDragStarted(); + + // Notify the listener_, if not NULL, that dragging ended. + void OnSliderDragEnded(); + // views::View overrides: - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) 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 bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; + virtual void OnFocus() OVERRIDE; + virtual void OnBlur() OVERRIDE; // ui::EventHandler overrides: virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; @@ -96,6 +110,10 @@ class VIEWS_EXPORT Slider : public View, public gfx::AnimationDelegate { // gfx::AnimationDelegate overrides: virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE; + void set_listener(SliderListener* listener) { + listener_ = listener; + } + SliderListener* listener_; Orientation orientation_; @@ -105,7 +123,7 @@ class VIEWS_EXPORT Slider : public View, public gfx::AnimationDelegate { float keyboard_increment_; float animating_value_; bool value_is_valid_; - string16 accessible_name_; + base::string16 accessible_name_; bool accessibility_events_enabled_; SkColor focus_border_color_; diff --git a/chromium/ui/views/controls/slider_unittest.cc b/chromium/ui/views/controls/slider_unittest.cc index 7add734ec36..da6700e0f72 100644 --- a/chromium/ui/views/controls/slider_unittest.cc +++ b/chromium/ui/views/controls/slider_unittest.cc @@ -4,73 +4,381 @@ #include "ui/views/controls/slider.h" +#include <string> + #include "base/memory/scoped_ptr.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" +#include "base/time/time.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/aura/test/event_generator.h" +#include "ui/aura/window.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/events/event.h" +#include "ui/events/gesture_event_details.h" +#include "ui/views/test/slider_test_api.h" +#include "ui/views/test/views_test_base.h" #include "ui/views/view.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" namespace { -void ClickAt(views::View* view, int x, int y) { - gfx::Point point(x, y); - ui::MouseEvent press(ui::ET_MOUSE_PRESSED, point, point, - ui::EF_LEFT_MOUSE_BUTTON); - view->OnMousePressed(press); - ui::MouseEvent release(ui::ET_MOUSE_RELEASED, point, point, - ui::EF_LEFT_MOUSE_BUTTON); - view->OnMouseReleased(release); +// A views::SliderListener that tracks simple event call history. +class TestSliderListener : public views::SliderListener { + public: + TestSliderListener(); + virtual ~TestSliderListener(); + + int last_event_epoch() { + return last_event_epoch_; + } + + int last_drag_started_epoch() { + return last_drag_started_epoch_; + } + + int last_drag_ended_epoch() { + return last_drag_ended_epoch_; + } + + views::Slider* last_drag_started_sender() { + return last_drag_started_sender_; + } + + views::Slider* last_drag_ended_sender() { + return last_drag_ended_sender_; + } + + // Resets the state of this as if it were newly created. + virtual void ResetCallHistory(); + + // views::SliderListener: + virtual void SliderValueChanged(views::Slider* sender, + float value, + float old_value, + views::SliderChangeReason reason) OVERRIDE; + virtual void SliderDragStarted(views::Slider* sender) OVERRIDE; + virtual void SliderDragEnded(views::Slider* sender) OVERRIDE; + + private: + // The epoch of the last event. + int last_event_epoch_; + // The epoch of the last time SliderDragStarted was called. + int last_drag_started_epoch_; + // The epoch of the last time SliderDragEnded was called. + int last_drag_ended_epoch_; + // The sender from the last SliderDragStarted call. + views::Slider* last_drag_started_sender_; + // The sender from the last SliderDragEnded call. + views::Slider* last_drag_ended_sender_; + + DISALLOW_COPY_AND_ASSIGN(TestSliderListener); +}; + +TestSliderListener::TestSliderListener() + : last_event_epoch_(0), + last_drag_started_epoch_(-1), + last_drag_ended_epoch_(-1), + last_drag_started_sender_(NULL), + last_drag_ended_sender_(NULL) { +} + +TestSliderListener::~TestSliderListener() { + last_drag_started_sender_ = NULL; + last_drag_ended_sender_ = NULL; +} + +void TestSliderListener::ResetCallHistory() { + last_event_epoch_ = 0; + last_drag_started_epoch_ = -1; + last_drag_ended_epoch_ = -1; + last_drag_started_sender_ = NULL; + last_drag_ended_sender_ = NULL; +} + +void TestSliderListener::SliderValueChanged(views::Slider* sender, + float value, + float old_value, + views::SliderChangeReason reason) { + ++last_event_epoch_; +} + +void TestSliderListener::SliderDragStarted(views::Slider* sender) { + last_drag_started_sender_ = sender; + last_drag_started_epoch_ = ++last_event_epoch_; } +void TestSliderListener::SliderDragEnded(views::Slider* sender) { + last_drag_ended_sender_ = sender; + last_drag_ended_epoch_ = ++last_event_epoch_; } +} // namespace + namespace views { -TEST(SliderTest, UpdateFromClickHorizontal) { - scoped_ptr<Slider> slider(new Slider(NULL, Slider::HORIZONTAL)); - View* view = slider.get(); - gfx::Size size = view->GetPreferredSize(); - view->SetSize(size); +// Base test fixture for Slider tests. +class SliderTest : public views::ViewsTestBase { + public: + explicit SliderTest(Slider::Orientation orientation); + virtual ~SliderTest(); + + protected: + Slider* slider() { + return slider_; + } + + TestSliderListener& slider_listener() { + return slider_listener_; + } + + int max_x() { + return max_x_; + } + + int max_y() { + return max_y_; + } + + virtual void ClickAt(int x, int y); + + // testing::Test: + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; + + aura::test::EventGenerator* event_generator() { + return event_generator_.get(); + } - ClickAt(view, 0, 0); - EXPECT_EQ(0.0f, slider->value()); + private: + // The Slider's orientation + Slider::Orientation orientation_; + // The Slider to be tested. + Slider* slider_; + // A simple SliderListener test double. + TestSliderListener slider_listener_; + // Stores the default locale at test setup so it can be restored + // during test teardown. + std::string default_locale_; + // The maximum x value within the bounds of the slider. + int max_x_; + // The maximum y value within the bounds of the slider. + int max_y_; + // The widget container for the slider being tested. + views::Widget* widget_; + // An event generator. + scoped_ptr<aura::test::EventGenerator> event_generator_; - ClickAt(view, view->width(), 0); - EXPECT_EQ(1.0f, slider->value()); + DISALLOW_COPY_AND_ASSIGN(SliderTest); +}; + +SliderTest::SliderTest(Slider::Orientation orientation) + : orientation_(orientation), + slider_(NULL), + default_locale_(""), + max_x_(0), + max_y_(0) { +} + +SliderTest::~SliderTest() { } -TEST(SliderTest, UpdateFromClickVertical) { - scoped_ptr<Slider> slider(new Slider(NULL, Slider::VERTICAL)); - View* view = slider.get(); +void SliderTest::SetUp() { + views::ViewsTestBase::SetUp(); + + slider_ = new Slider(NULL, orientation_); + View* view = slider_; gfx::Size size = view->GetPreferredSize(); view->SetSize(size); + max_x_ = size.width() - 1; + max_y_ = size.height() - 1; + default_locale_ = l10n_util::GetApplicationLocale(""); + + views::Widget::InitParams init_params(CreateParams( + views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)); + init_params.bounds = gfx::Rect(size); - ClickAt(view, 0, 0); - EXPECT_EQ(1.0f, slider->value()); + widget_ = new views::Widget(); + widget_->Init(init_params); + widget_->SetContentsView(slider_); + widget_->Show(); - ClickAt(view, 0, view->height()); - EXPECT_EQ(0.0f, slider->value()); + aura::Window* native_window = widget_->GetNativeWindow(); + event_generator_.reset(new aura::test::EventGenerator( + native_window->GetRootWindow())); } -TEST(SliderTest, UpdateFromClickRTLHorizontal) { - std::string locale = l10n_util::GetApplicationLocale(""); +void SliderTest::TearDown() { + if (widget_ && !widget_->IsClosed()) + widget_->Close(); + + base::i18n::SetICUDefaultLocale(default_locale_); + + views::ViewsTestBase::TearDown(); +} + +void SliderTest::ClickAt(int x, int y) { + gfx::Point point(x, y); + event_generator_->MoveMouseTo(point); + event_generator_->ClickLeftButton(); +} + +// Test fixture for horizontally oriented slider tests. +class HorizontalSliderTest : public SliderTest { + public: + HorizontalSliderTest(); + virtual ~HorizontalSliderTest(); + + private: + DISALLOW_COPY_AND_ASSIGN(HorizontalSliderTest); +}; + +HorizontalSliderTest::HorizontalSliderTest() + : SliderTest(Slider::HORIZONTAL) { +} + +HorizontalSliderTest::~HorizontalSliderTest() { +} + +// Test fixture for vertically oriented slider tests. +class VerticalSliderTest : public SliderTest { + public: + VerticalSliderTest(); + virtual ~VerticalSliderTest(); + + private: + DISALLOW_COPY_AND_ASSIGN(VerticalSliderTest); +}; + +VerticalSliderTest::VerticalSliderTest() + : SliderTest(Slider::VERTICAL) { +} + +VerticalSliderTest::~VerticalSliderTest() { +} + +TEST_F(HorizontalSliderTest, UpdateFromClickHorizontal) { + ClickAt(0, 0); + EXPECT_EQ(0.0f, slider()->value()); + + ClickAt(max_x(), 0); + EXPECT_EQ(1.0f, slider()->value()); +} + +TEST_F(VerticalSliderTest, UpdateFromClickVertical) { + ClickAt(0, 0); + EXPECT_EQ(1.0f, slider()->value()); + + ClickAt(0, max_y()); + EXPECT_EQ(0.0f, slider()->value()); +} + +TEST_F(HorizontalSliderTest, UpdateFromClickRTLHorizontal) { base::i18n::SetICUDefaultLocale("he"); - scoped_ptr<Slider> slider(new Slider(NULL, Slider::HORIZONTAL)); - View* view = slider.get(); - gfx::Size size = view->GetPreferredSize(); - view->SetSize(size); + ClickAt(0, 0); + EXPECT_EQ(1.0f, slider()->value()); + + ClickAt(max_x(), 0); + EXPECT_EQ(0.0f, slider()->value()); +} + +// Test the slider location after a tap gesture. +TEST_F(HorizontalSliderTest, SliderValueForTapGesture) { + // Tap below the minimum. + slider()->SetValue(0.5); + event_generator()->GestureTapAt(gfx::Point(0, 0)); + EXPECT_FLOAT_EQ(0, slider()->value()); + + // Tap above the maximum. + slider()->SetValue(0.5); + event_generator()->GestureTapAt(gfx::Point(max_x(), max_y())); + EXPECT_FLOAT_EQ(1, slider()->value()); + + // Tap somwhere in the middle. + slider()->SetValue(0.5); + event_generator()->GestureTapAt(gfx::Point(0.75 * max_x(), 0.75 * max_y())); + EXPECT_NEAR(0.75, slider()->value(), 0.03); +} + +// Test the slider location after a scroll gesture. +TEST_F(HorizontalSliderTest, SliderValueForScrollGesture) { + // Scroll below the minimum. + slider()->SetValue(0.5); + event_generator()->GestureScrollSequence( + gfx::Point(0.5 * max_x(), 0.5 * max_y()), + gfx::Point(0, 0), + base::TimeDelta::FromMilliseconds(10), + 5 /* steps */); + EXPECT_EQ(0, slider()->value()); + + // Scroll above the maximum. + slider()->SetValue(0.5); + event_generator()->GestureScrollSequence( + gfx::Point(0.5 * max_x(), 0.5 * max_y()), + gfx::Point(max_x(), max_y()), + base::TimeDelta::FromMilliseconds(10), + 5 /* steps */); + EXPECT_EQ(1, slider()->value()); + + // Scroll somewhere in the middle. + slider()->SetValue(0.25); + event_generator()->GestureScrollSequence( + gfx::Point(0.25 * max_x(), 0.25 * max_y()), + gfx::Point(0.75 * max_x(), 0.75 * max_y()), + base::TimeDelta::FromMilliseconds(10), + 5 /* steps */); + EXPECT_NEAR(0.75, slider()->value(), 0.03); +} + +// Verifies the correct SliderListener events are raised for a tap gesture. +TEST_F(HorizontalSliderTest, SliderListenerEventsForTapGesture) { + test::SliderTestApi slider_test_api(slider()); + slider_test_api.SetListener(&slider_listener()); + + event_generator()->GestureTapAt(gfx::Point(0, 0)); + EXPECT_EQ(1, slider_listener().last_drag_started_epoch()); + EXPECT_EQ(2, slider_listener().last_drag_ended_epoch()); + EXPECT_EQ(slider(), slider_listener().last_drag_started_sender()); + EXPECT_EQ(slider(), slider_listener().last_drag_ended_sender()); +} + +// Verifies the correct SliderListener events are raised for a scroll gesture. +TEST_F(HorizontalSliderTest, SliderListenerEventsForScrollGesture) { + test::SliderTestApi slider_test_api(slider()); + slider_test_api.SetListener(&slider_listener()); + + event_generator()->GestureScrollSequence( + gfx::Point(0.25 * max_x(), 0.25 * max_y()), + gfx::Point(0.75 * max_x(), 0.75 * max_y()), + base::TimeDelta::FromMilliseconds(0), + 5 /* steps */); + + EXPECT_EQ(1, slider_listener().last_drag_started_epoch()); + EXPECT_GT(slider_listener().last_drag_ended_epoch(), + slider_listener().last_drag_started_epoch()); + EXPECT_EQ(slider(), slider_listener().last_drag_started_sender()); + EXPECT_EQ(slider(), slider_listener().last_drag_ended_sender()); +} - ClickAt(view, 0, 0); - EXPECT_EQ(1.0f, slider->value()); +// Verifies the correct SliderListener events are raised for a multi +// finger scroll gesture. +TEST_F(HorizontalSliderTest, SliderListenerEventsForMultiFingerScrollGesture) { + test::SliderTestApi slider_test_api(slider()); + slider_test_api.SetListener(&slider_listener()); - ClickAt(view, view->width(), 0); - EXPECT_EQ(0.0f, slider->value()); + gfx::Point points[] = {gfx::Point(0, 0.1 * max_y()), + gfx::Point(0, 0.2 * max_y())}; + event_generator()->GestureMultiFingerScroll(2 /* count */, points, + 0 /* event_separation_time_ms */, 5 /* steps */, + 2 /* move_x */, 0 /* move_y */); - // Reset locale. - base::i18n::SetICUDefaultLocale(locale); + EXPECT_EQ(1, slider_listener().last_drag_started_epoch()); + EXPECT_GT(slider_listener().last_drag_ended_epoch(), + slider_listener().last_drag_started_epoch()); + EXPECT_EQ(slider(), slider_listener().last_drag_started_sender()); + EXPECT_EQ(slider(), slider_listener().last_drag_ended_sender()); } } // namespace views diff --git a/chromium/ui/views/controls/styled_label.cc b/chromium/ui/views/controls/styled_label.cc index 96cc275c2d1..e7ee56c0b44 100644 --- a/chromium/ui/views/controls/styled_label.cc +++ b/chromium/ui/views/controls/styled_label.cc @@ -7,6 +7,7 @@ #include <vector> #include "base/strings/string_util.h" +#include "ui/gfx/font_list.h" #include "ui/gfx/text_elider.h" #include "ui/native_theme/native_theme.h" #include "ui/views/controls/label.h" @@ -15,17 +16,22 @@ namespace views { + +// Helpers -------------------------------------------------------------------- + namespace { // Calculates the height of a line of text. Currently returns the height of // a label. -int CalculateLineHeight() { +int CalculateLineHeight(const gfx::FontList& font_list) { Label label; + label.SetFontList(font_list); return label.GetPreferredSize().height(); } scoped_ptr<Label> CreateLabelRange( - const string16& text, + const base::string16& text, + const gfx::FontList& font_list, const StyledLabel::RangeStyleInfo& style_info, views::LinkListener* link_listener) { scoped_ptr<Label> result; @@ -36,26 +42,27 @@ scoped_ptr<Label> CreateLabelRange( link->SetUnderline((style_info.font_style & gfx::Font::UNDERLINE) != 0); result.reset(link); } else { - Label* label = new Label(text); - // Give the label a focus border so that its preferred size matches - // links' preferred sizes - label->SetHasFocusBorder(true); - - result.reset(label); + result.reset(new Label(text)); } result->SetEnabledColor(style_info.color); + result->SetFontList(font_list); if (!style_info.tooltip.empty()) result->SetTooltipText(style_info.tooltip); - if (style_info.font_style != gfx::Font::NORMAL) - result->SetFont(result->font().DeriveFont(0, style_info.font_style)); + if (style_info.font_style != gfx::Font::NORMAL) { + result->SetFontList( + result->font_list().DeriveWithStyle(style_info.font_style)); + } - return scoped_ptr<Label>(result.release()); + return result.Pass(); } } // namespace + +// StyledLabel::RangeStyleInfo ------------------------------------------------ + StyledLabel::RangeStyleInfo::RangeStyleInfo() : font_style(gfx::Font::NORMAL), color(ui::NativeTheme::instance()->GetSystemColor( @@ -74,35 +81,49 @@ StyledLabel::RangeStyleInfo StyledLabel::RangeStyleInfo::CreateForLink() { return result; } + +// StyledLabel::StyleRange ---------------------------------------------------- + bool StyledLabel::StyleRange::operator<( const StyledLabel::StyleRange& other) const { - // Intentionally reversed so the priority queue is sorted by smallest first. - return range.start() > other.range.start(); + return range.start() < other.range.start(); } -StyledLabel::StyledLabel(const string16& text, StyledLabelListener* listener) + +// StyledLabel ---------------------------------------------------------------- + +StyledLabel::StyledLabel(const base::string16& text, + StyledLabelListener* listener) : listener_(listener), displayed_on_background_color_set_(false), auto_color_readability_enabled_(true) { - TrimWhitespace(text, TRIM_TRAILING, &text_); + base::TrimWhitespace(text, base::TRIM_TRAILING, &text_); } StyledLabel::~StyledLabel() {} -void StyledLabel::SetText(const string16& text) { +void StyledLabel::SetText(const base::string16& text) { text_ = text; - style_ranges_ = std::priority_queue<StyleRange>(); + style_ranges_.clear(); RemoveAllChildViews(true); PreferredSizeChanged(); } +void StyledLabel::SetBaseFontList(const gfx::FontList& font_list) { + font_list_ = font_list; + PreferredSizeChanged(); +} + void StyledLabel::AddStyleRange(const gfx::Range& range, const RangeStyleInfo& style_info) { DCHECK(!range.is_reversed()); DCHECK(!range.is_empty()); DCHECK(gfx::Range(0, text_.size()).Contains(range)); - style_ranges_.push(StyleRange(range, style_info)); + // Insert the new range in sorted order. + StyleRanges new_range; + new_range.push_front(StyleRange(range, style_info)); + style_ranges_.merge(new_range); PreferredSizeChanged(); } @@ -119,20 +140,37 @@ void StyledLabel::SetDisplayedOnBackgroundColor(SkColor color) { gfx::Insets StyledLabel::GetInsets() const { gfx::Insets insets = View::GetInsets(); - const gfx::Insets focus_border_padding(1, 1, 1, 1); - insets += focus_border_padding; + + // We need a focus border iff we contain a link that will have a focus border. + // That in turn will be true only if the link is non-empty. + for (StyleRanges::const_iterator i(style_ranges_.begin()); + i != style_ranges_.end(); ++i) { + if (i->style_info.is_link && !i->range.is_empty()) { + const gfx::Insets focus_border_padding( + Label::kFocusBorderPadding, Label::kFocusBorderPadding, + Label::kFocusBorderPadding, Label::kFocusBorderPadding); + insets += focus_border_padding; + break; + } + } + return insets; } -int StyledLabel::GetHeightForWidth(int w) { - if (w != calculated_size_.width()) - calculated_size_ = gfx::Size(w, CalculateAndDoLayout(w, true)); - +int StyledLabel::GetHeightForWidth(int w) const { + if (w != calculated_size_.width()) { + // TODO(erg): Munge the const-ness of the style label. CalculateAndDoLayout + // doesn't actually make any changes to member variables when |dry_run| is + // set to true. In general, the mutating and non-mutating parts shouldn't + // be in the same codepath. + calculated_size_ = + const_cast<StyledLabel*>(this)->CalculateAndDoLayout(w, true); + } return calculated_size_.height(); } void StyledLabel::Layout() { - CalculateAndDoLayout(GetLocalBounds().width(), false); + calculated_size_ = CalculateAndDoLayout(GetLocalBounds().width(), false); } void StyledLabel::PreferredSizeChanged() { @@ -145,7 +183,7 @@ void StyledLabel::LinkClicked(Link* source, int event_flags) { listener_->StyledLabelLinkClicked(link_targets_[source], event_flags); } -int StyledLabel::CalculateAndDoLayout(int width, bool dry_run) { +gfx::Size StyledLabel::CalculateAndDoLayout(int width, bool dry_run) { if (!dry_run) { RemoveAllChildViews(true); link_targets_.clear(); @@ -153,41 +191,43 @@ int StyledLabel::CalculateAndDoLayout(int width, bool dry_run) { width -= GetInsets().width(); if (width <= 0 || text_.empty()) - return 0; + return gfx::Size(); - const int line_height = CalculateLineHeight(); + const int line_height = CalculateLineHeight(font_list_); // The index of the line we're on. int line = 0; // The x position (in pixels) of the line we're on, relative to content // bounds. int x = 0; - string16 remaining_string = text_; - std::priority_queue<StyleRange> style_ranges = style_ranges_; + base::string16 remaining_string = text_; + StyleRanges::const_iterator current_range = style_ranges_.begin(); // Iterate over the text, creating a bunch of labels and links and laying them // out in the appropriate positions. while (!remaining_string.empty()) { // Don't put whitespace at beginning of a line with an exception for the // first line (so the text's leading whitespace is respected). - if (x == 0 && line > 0) - TrimWhitespace(remaining_string, TRIM_LEADING, &remaining_string); + if (x == 0 && line > 0) { + base::TrimWhitespace(remaining_string, base::TRIM_LEADING, + &remaining_string); + } gfx::Range range(gfx::Range::InvalidRange()); - if (!style_ranges.empty()) - range = style_ranges.top().range; + if (current_range != style_ranges_.end()) + range = current_range->range; const size_t position = text_.size() - remaining_string.size(); const gfx::Rect chunk_bounds(x, 0, width - x, 2 * line_height); - std::vector<string16> substrings; - gfx::FontList text_font_list; + std::vector<base::string16> substrings; + gfx::FontList text_font_list = font_list_; // If the start of the remaining text is inside a styled range, the font // style may differ from the base font. The font specified by the range // should be used when eliding text. if (position >= range.start()) { - text_font_list = text_font_list.DeriveFontListWithSizeDeltaAndStyle( - 0, style_ranges.top().style_info.font_style); + text_font_list = text_font_list.DeriveWithStyle( + current_range->style_info.font_style); } gfx::ElideRectangleText(remaining_string, text_font_list, @@ -197,7 +237,7 @@ int StyledLabel::CalculateAndDoLayout(int width, bool dry_run) { &substrings); DCHECK(!substrings.empty()); - string16 chunk = substrings[0]; + base::string16 chunk = substrings[0]; if (chunk.empty()) { // Nothing fits on this line. Start a new line. // If x is 0, first line may have leading whitespace that doesn't fit in a @@ -205,7 +245,8 @@ int StyledLabel::CalculateAndDoLayout(int width, bool dry_run) { // anything; abort. if (x == 0) { if (line == 0) { - TrimWhitespace(remaining_string, TRIM_LEADING, &remaining_string); + base::TrimWhitespace(remaining_string, base::TRIM_LEADING, + &remaining_string); continue; } break; @@ -218,7 +259,7 @@ int StyledLabel::CalculateAndDoLayout(int width, bool dry_run) { scoped_ptr<Label> label; if (position >= range.start()) { - const RangeStyleInfo& style_info = style_ranges.top().style_info; + const RangeStyleInfo& style_info = current_range->style_info; if (style_info.disable_line_wrapping && chunk.size() < range.length() && position == range.start() && x != 0) { @@ -231,42 +272,44 @@ int StyledLabel::CalculateAndDoLayout(int width, bool dry_run) { chunk = chunk.substr(0, std::min(chunk.size(), range.end() - position)); - label = CreateLabelRange(chunk, style_info, this); + label = CreateLabelRange(chunk, font_list_, style_info, this); if (style_info.is_link && !dry_run) link_targets_[label.get()] = range; if (position + chunk.size() >= range.end()) - style_ranges.pop(); + ++current_range; } else { // This chunk is normal text. if (position + chunk.size() > range.start()) chunk = chunk.substr(0, range.start() - position); - label = CreateLabelRange(chunk, default_style_info_, this); + label = CreateLabelRange(chunk, font_list_, default_style_info_, this); } if (displayed_on_background_color_set_) label->SetBackgroundColor(displayed_on_background_color_); label->SetAutoColorReadabilityEnabled(auto_color_readability_enabled_); - // Lay out the views to overlap by 1 pixel to compensate for their border - // spacing. Otherwise, "<a>link</a>," will render as "link ,". - const int overlap = 1; + // Calculate the size of the optional focus border, and overlap by that + // amount. Otherwise, "<a>link</a>," will render as "link ,". + gfx::Insets focus_border_insets(label->GetInsets()); + focus_border_insets += -label->View::GetInsets(); const gfx::Size view_size = label->GetPreferredSize(); - DCHECK_EQ(line_height, view_size.height() - 2 * overlap); + DCHECK_EQ(line_height, view_size.height() - focus_border_insets.height()); if (!dry_run) { label->SetBoundsRect(gfx::Rect( - gfx::Point(GetInsets().left() + x - overlap, - GetInsets().top() + line * line_height - overlap), + gfx::Point(GetInsets().left() + x - focus_border_insets.left(), + GetInsets().top() + line * line_height - + focus_border_insets.top()), view_size)); AddChildView(label.release()); } - x += view_size.width() - 2 * overlap; + x += view_size.width() - focus_border_insets.width(); remaining_string = remaining_string.substr(chunk.size()); } - return (line + 1) * line_height + GetInsets().height(); + return gfx::Size(width, (line + 1) * line_height + GetInsets().height()); } } // namespace views diff --git a/chromium/ui/views/controls/styled_label.h b/chromium/ui/views/controls/styled_label.h index adf5993a767..4c599a0d438 100644 --- a/chromium/ui/views/controls/styled_label.h +++ b/chromium/ui/views/controls/styled_label.h @@ -5,12 +5,13 @@ #ifndef UI_VIEWS_CONTROLS_STYLED_LABEL_H_ #define UI_VIEWS_CONTROLS_STYLED_LABEL_H_ +#include <list> #include <map> -#include <queue> #include "base/basictypes.h" #include "base/strings/string16.h" #include "third_party/skia/include/core/SkColor.h" +#include "ui/gfx/font_list.h" #include "ui/gfx/range/range.h" #include "ui/gfx/size.h" #include "ui/views/controls/link_listener.h" @@ -44,7 +45,7 @@ class VIEWS_EXPORT StyledLabel : public View, public LinkListener { SkColor color; // Tooltip for the range. - string16 tooltip; + base::string16 tooltip; // If set, the whole range will be put on a single line. bool disable_line_wrapping; @@ -54,11 +55,15 @@ class VIEWS_EXPORT StyledLabel : public View, public LinkListener { }; // Note that any trailing whitespace in |text| will be trimmed. - StyledLabel(const string16& text, StyledLabelListener* listener); + StyledLabel(const base::string16& text, StyledLabelListener* listener); virtual ~StyledLabel(); // Sets the text to be displayed, and clears any previous styling. - void SetText(const string16& text); + void SetText(const base::string16& text); + + // Sets the fonts used by all labels. Can be augemented by styling set by + // AddStyleRange and SetDefaultStyle. + void SetBaseFontList(const gfx::FontList& font_list); // Marks the given range within |text_| with style defined by |style_info|. // |range| must be contained in |text_|. @@ -82,7 +87,7 @@ class VIEWS_EXPORT StyledLabel : public View, public LinkListener { // View implementation: virtual gfx::Insets GetInsets() const OVERRIDE; - virtual int GetHeightForWidth(int w) OVERRIDE; + virtual int GetHeightForWidth(int w) const OVERRIDE; virtual void Layout() OVERRIDE; virtual void PreferredSizeChanged() OVERRIDE; @@ -103,15 +108,19 @@ class VIEWS_EXPORT StyledLabel : public View, public LinkListener { gfx::Range range; RangeStyleInfo style_info; }; + typedef std::list<StyleRange> StyleRanges; // Calculates how to layout child views, creates them and sets their size // and position. |width| is the horizontal space, in pixels, that the view // has to work with. If |dry_run| is true, the view hierarchy is not touched. - // The return value is the height in pixels. - int CalculateAndDoLayout(int width, bool dry_run); + // The return value is the necessary size. + gfx::Size CalculateAndDoLayout(int width, bool dry_run); // The text to display. - string16 text_; + base::string16 text_; + + // Fonts used to display text. Can be augmented by RangeStyleInfo. + gfx::FontList font_list_; // The default style to use for any part of the text that isn't within // a range in |style_ranges_|. @@ -121,7 +130,7 @@ class VIEWS_EXPORT StyledLabel : public View, public LinkListener { StyledLabelListener* listener_; // The ranges that should be linkified, sorted by start position. - std::priority_queue<StyleRange> style_ranges_; + StyleRanges style_ranges_; // A mapping from a view to the range it corresponds to in |text_|. Only views // that correspond to ranges with is_link style set will be added to the map. @@ -129,7 +138,7 @@ class VIEWS_EXPORT StyledLabel : public View, public LinkListener { // This variable saves the result of the last GetHeightForWidth call in order // to avoid repeated calculation. - gfx::Size calculated_size_; + mutable gfx::Size calculated_size_; // Background color on which the label is drawn, for auto color readability. SkColor displayed_on_background_color_; diff --git a/chromium/ui/views/controls/styled_label_unittest.cc b/chromium/ui/views/controls/styled_label_unittest.cc index 18595edf3d5..d360aedb1b0 100644 --- a/chromium/ui/views/controls/styled_label_unittest.cc +++ b/chromium/ui/views/controls/styled_label_unittest.cc @@ -9,14 +9,19 @@ #include "base/strings/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkColor.h" +#include "ui/gfx/font_list.h" #include "ui/views/border.h" #include "ui/views/controls/link.h" #include "ui/views/controls/styled_label.h" #include "ui/views/controls/styled_label_listener.h" +#include "ui/views/test/views_test_base.h" +#include "ui/views/widget/widget.h" + +using base::ASCIIToUTF16; namespace views { -class StyledLabelTest : public testing::Test, public StyledLabelListener { +class StyledLabelTest : public ViewsTestBase, public StyledLabelListener { public: StyledLabelTest() {} virtual ~StyledLabelTest() {} @@ -30,6 +35,7 @@ class StyledLabelTest : public testing::Test, public StyledLabelListener { void InitStyledLabel(const std::string& ascii_text) { styled_.reset(new StyledLabel(ASCIIToUTF16(ascii_text), this)); + styled_->set_owned_by_client(); } int StyledLabelContentHeightForWidth(int w) { @@ -105,7 +111,7 @@ TEST_F(StyledLabelTest, BasicWrapping) { StyledLabelContentHeightForWidth(label_preferred_size.width())); // Also respect the border. - styled()->set_border(Border::CreateEmptyBorder(3, 3, 3, 3)); + styled()->SetBorder(Border::CreateEmptyBorder(3, 3, 3, 3)); styled()->SetBounds( 0, 0, @@ -113,15 +119,19 @@ TEST_F(StyledLabelTest, BasicWrapping) { styled()->GetInsets().height() + 2 * label_preferred_size.height()); styled()->Layout(); ASSERT_EQ(2, styled()->child_count()); - EXPECT_EQ(3, styled()->child_at(0)->bounds().x()); - EXPECT_EQ(3, styled()->child_at(0)->bounds().y()); - EXPECT_EQ(styled()->bounds().height() - 3, - styled()->child_at(1)->bounds().bottom()); + EXPECT_EQ(3, styled()->child_at(0)->x()); + EXPECT_EQ(3, styled()->child_at(0)->y()); + EXPECT_EQ(styled()->height() - 3, styled()->child_at(1)->bounds().bottom()); } TEST_F(StyledLabelTest, CreateLinks) { const std::string text("This is a test block of text."); InitStyledLabel(text); + + // Without links, there should be no focus border. + EXPECT_TRUE(styled()->GetInsets().empty()); + + // Now let's add some links. styled()->AddStyleRange(gfx::Range(0, 1), StyledLabel::RangeStyleInfo::CreateForLink()); styled()->AddStyleRange(gfx::Range(1, 2), @@ -131,9 +141,13 @@ TEST_F(StyledLabelTest, CreateLinks) { styled()->AddStyleRange(gfx::Range(12, 13), StyledLabel::RangeStyleInfo::CreateForLink()); + // Now there should be a focus border because there are non-empty Links. + EXPECT_FALSE(styled()->GetInsets().empty()); + + // Verify layout creates the right number of children. styled()->SetBounds(0, 0, 1000, 1000); styled()->Layout(); - ASSERT_EQ(7, styled()->child_count()); + EXPECT_EQ(7, styled()->child_count()); } TEST_F(StyledLabelTest, DontBreakLinks) { @@ -153,13 +167,15 @@ TEST_F(StyledLabelTest, DontBreakLinks) { styled()->SetBounds(0, 0, label_preferred_size.width(), pref_height); styled()->Layout(); ASSERT_EQ(2, styled()->child_count()); - EXPECT_EQ(0, styled()->child_at(0)->bounds().x()); - EXPECT_EQ(0, styled()->child_at(1)->bounds().x()); + // The label has no focus border while the link (and thus overall styled + // label) does, so the label should be inset by the width of the focus border. + EXPECT_EQ(Label::kFocusBorderPadding, styled()->child_at(0)->x()); + EXPECT_EQ(0, styled()->child_at(1)->x()); } TEST_F(StyledLabelTest, StyledRangeWithDisabledLineWrapping) { const std::string text("This is a test block of text, "); - const std::string unbreakable_text("and this should not be breaked"); + const std::string unbreakable_text("and this should not be broken"); InitStyledLabel(text + unbreakable_text); StyledLabel::RangeStyleInfo style_info; style_info.disable_line_wrapping = true; @@ -177,8 +193,8 @@ TEST_F(StyledLabelTest, StyledRangeWithDisabledLineWrapping) { styled()->SetBounds(0, 0, label_preferred_size.width(), pref_height); styled()->Layout(); ASSERT_EQ(2, styled()->child_count()); - EXPECT_EQ(0, styled()->child_at(0)->bounds().x()); - EXPECT_EQ(0, styled()->child_at(1)->bounds().x()); + EXPECT_EQ(0, styled()->child_at(0)->x()); + EXPECT_EQ(0, styled()->child_at(1)->x()); } TEST_F(StyledLabelTest, StyledRangeUnderlined) { @@ -197,8 +213,9 @@ TEST_F(StyledLabelTest, StyledRangeUnderlined) { ASSERT_EQ(2, styled()->child_count()); ASSERT_EQ(std::string(Label::kViewClassName), styled()->child_at(1)->GetClassName()); - EXPECT_EQ(gfx::Font::UNDERLINE, - static_cast<Label*>(styled()->child_at(1))->font().GetStyle()); + EXPECT_EQ( + gfx::Font::UNDERLINE, + static_cast<Label*>(styled()->child_at(1))->font_list().GetFontStyle()); } TEST_F(StyledLabelTest, StyledRangeBold) { @@ -215,7 +232,7 @@ TEST_F(StyledLabelTest, StyledRangeBold) { // and normal style. Label label(ASCIIToUTF16(bold_text)); const gfx::Size normal_label_size = label.GetPreferredSize(); - label.SetFont(label.font().DeriveFont(0, gfx::Font::BOLD)); + label.SetFontList(label.font_list().DeriveWithStyle(gfx::Font::BOLD)); const gfx::Size bold_label_size = label.GetPreferredSize(); ASSERT_GE(bold_label_size.width(), normal_label_size.width()); @@ -230,7 +247,7 @@ TEST_F(StyledLabelTest, StyledRangeBold) { StyledLabel unstyled(ASCIIToUTF16(bold_text), this); unstyled.SetBounds(0, 0, styled_width, pref_height); unstyled.Layout(); - ASSERT_EQ(1, unstyled.child_count()); + EXPECT_EQ(1, unstyled.child_count()); styled()->SetBounds(0, 0, styled_width, pref_height); styled()->Layout(); @@ -240,22 +257,25 @@ TEST_F(StyledLabelTest, StyledRangeBold) { // The bold text should be broken up into two parts. ASSERT_EQ(std::string(Label::kViewClassName), styled()->child_at(0)->GetClassName()); - EXPECT_EQ(gfx::Font::BOLD, - static_cast<Label*>(styled()->child_at(0))->font().GetStyle()); + EXPECT_EQ( + gfx::Font::BOLD, + static_cast<Label*>(styled()->child_at(0))->font_list().GetFontStyle()); ASSERT_EQ(std::string(Label::kViewClassName), styled()->child_at(1)->GetClassName()); - EXPECT_EQ(gfx::Font::BOLD, - static_cast<Label*>(styled()->child_at(1))->font().GetStyle()); + EXPECT_EQ( + gfx::Font::BOLD, + static_cast<Label*>(styled()->child_at(1))->font_list().GetFontStyle()); ASSERT_EQ(std::string(Label::kViewClassName), styled()->child_at(2)->GetClassName()); - EXPECT_EQ(gfx::Font::NORMAL, - static_cast<Label*>(styled()->child_at(2))->font().GetStyle()); + EXPECT_EQ( + gfx::Font::NORMAL, + static_cast<Label*>(styled()->child_at(2))->font_list().GetFontStyle()); // The second bold part should start on a new line. - EXPECT_EQ(0, styled()->child_at(0)->bounds().x()); - EXPECT_EQ(0, styled()->child_at(1)->bounds().x()); - EXPECT_EQ(styled()->child_at(1)->bounds().right() - 2, - styled()->child_at(2)->bounds().x()); + EXPECT_EQ(0, styled()->child_at(0)->x()); + EXPECT_EQ(0, styled()->child_at(1)->x()); + EXPECT_EQ(styled()->child_at(1)->bounds().right(), + styled()->child_at(2)->x()); } TEST_F(StyledLabelTest, Color) { @@ -274,16 +294,25 @@ TEST_F(StyledLabelTest, Color) { text_red.size() + text_link.size()), style_info_link); + styled()->SetBounds(0, 0, 1000, 1000); + styled()->Layout(); + + Widget* widget = new Widget(); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + widget->Init(params); + View* container = new View(); + widget->SetContentsView(container); + container->AddChildView(styled()); + // Obtain the default text color for a label. - Label label(ASCIIToUTF16(text)); - const SkColor kDefaultTextColor = label.enabled_color(); + Label* label = new Label(ASCIIToUTF16(text)); + container->AddChildView(label); + const SkColor kDefaultTextColor = label->enabled_color(); // Obtain the default text color for a link; - Link link(ASCIIToUTF16(text_link)); - const SkColor kDefaultLinkColor = link.enabled_color(); - - styled()->SetBounds(0, 0, 1000, 1000); - styled()->Layout(); + Link* link = new Link(ASCIIToUTF16(text_link)); + container->AddChildView(link); + const SkColor kDefaultLinkColor = link->enabled_color(); EXPECT_EQ(SK_ColorRED, static_cast<Label*>(styled()->child_at(0))->enabled_color()); @@ -291,23 +320,18 @@ TEST_F(StyledLabelTest, Color) { static_cast<Label*>(styled()->child_at(1))->enabled_color()); EXPECT_EQ(kDefaultTextColor, static_cast<Label*>(styled()->child_at(2))->enabled_color()); -} -TEST_F(StyledLabelTest, ColorReadability) { - const std::string text( - "This is a block of text that needs color adjustment."); - InitStyledLabel(text); + // Test adjusted color readability. styled()->SetDisplayedOnBackgroundColor(SK_ColorBLACK); - - // Obtain the text color if it were a pure label. - Label label(ASCIIToUTF16(text)); - label.SetBackgroundColor(SK_ColorBLACK); - - styled()->SetBounds(0, 0, 1000, 1000); styled()->Layout(); + label->SetBackgroundColor(SK_ColorBLACK); - EXPECT_EQ(label.enabled_color(), - static_cast<Label*>(styled()->child_at(0))->enabled_color()); + const SkColor kAdjustedTextColor = label->enabled_color(); + EXPECT_NE(kAdjustedTextColor, kDefaultTextColor); + EXPECT_EQ(kAdjustedTextColor, + static_cast<Label*>(styled()->child_at(2))->enabled_color()); + + widget->CloseNow(); } TEST_F(StyledLabelTest, StyledRangeWithTooltip) { @@ -343,15 +367,18 @@ TEST_F(StyledLabelTest, StyledRangeWithTooltip) { EXPECT_EQ(label_preferred_size.width(), styled()->width()); ASSERT_EQ(5, styled()->child_count()); - EXPECT_EQ(0, styled()->child_at(0)->bounds().x()); - EXPECT_EQ(styled()->child_at(0)->bounds().right() - 2, - styled()->child_at(1)->bounds().x()); - EXPECT_EQ(0, styled()->child_at(2)->bounds().x()); - EXPECT_EQ(styled()->child_at(2)->bounds().right() - 2, - styled()->child_at(3)->bounds().x()); - EXPECT_EQ(0, styled()->child_at(4)->bounds().x()); - - string16 tooltip; + // The labels have no focus border while the link (and thus overall styled + // label) does, so the labels should be inset by the width of the focus + // border. + EXPECT_EQ(Label::kFocusBorderPadding, styled()->child_at(0)->x()); + EXPECT_EQ(styled()->child_at(0)->bounds().right(), + styled()->child_at(1)->x()); + EXPECT_EQ(Label::kFocusBorderPadding, styled()->child_at(2)->x()); + EXPECT_EQ(styled()->child_at(2)->bounds().right(), + styled()->child_at(3)->x()); + EXPECT_EQ(0, styled()->child_at(4)->x()); + + base::string16 tooltip; EXPECT_TRUE( styled()->child_at(1)->GetTooltipText(gfx::Point(1, 1), &tooltip)); EXPECT_EQ(ASCIIToUTF16("tooltip"), tooltip); @@ -360,11 +387,29 @@ TEST_F(StyledLabelTest, StyledRangeWithTooltip) { EXPECT_EQ(ASCIIToUTF16("tooltip"), tooltip); } +TEST_F(StyledLabelTest, SetBaseFontList) { + const std::string text("This is a test block of text."); + InitStyledLabel(text); + std::string font_name("arial"); + gfx::Font font(font_name, 30); + styled()->SetBaseFontList(gfx::FontList(font)); + Label label(ASCIIToUTF16(text), gfx::FontList(font)); + + styled()->SetBounds(0, + 0, + label.GetPreferredSize().width(), + label.GetPreferredSize().height()); + + // Make sure we have the same sizing as a label. + EXPECT_EQ(label.GetPreferredSize().height(), styled()->height()); + EXPECT_EQ(label.GetPreferredSize().width(), styled()->width()); +} + TEST_F(StyledLabelTest, HandleEmptyLayout) { - const std::string text("This is a test block of text, "); + const std::string text("This is a test block of text."); InitStyledLabel(text); styled()->Layout(); - ASSERT_EQ(0, styled()->child_count()); + EXPECT_EQ(0, styled()->child_count()); } -} // namespace +} // namespace views diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc index 3f2d5da96f5..3d181e0c8ce 100644 --- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc +++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc @@ -5,10 +5,11 @@ #include "ui/views/controls/tabbed_pane/tabbed_pane.h" #include "base/logging.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" +#include "ui/base/resource/resource_bundle.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/canvas.h" -#include "ui/gfx/font.h" +#include "ui/gfx/font_list.h" #include "ui/views/controls/label.h" #include "ui/views/controls/tabbed_pane/tabbed_pane_listener.h" #include "ui/views/layout/layout_manager.h" @@ -33,7 +34,7 @@ const char TabbedPane::kViewClassName[] = "TabbedPane"; // The tab view shown in the tab strip. class Tab : public View { public: - Tab(TabbedPane* tabbed_pane, const string16& title, View* contents); + Tab(TabbedPane* tabbed_pane, const base::string16& title, View* contents); virtual ~Tab(); View* contents() const { return contents_; } @@ -46,7 +47,7 @@ class Tab : public View { virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE; virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE; virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual void Layout() OVERRIDE; private: @@ -75,7 +76,7 @@ class TabStrip : public View { virtual ~TabStrip(); // Overridden from View: - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual void Layout() OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; @@ -85,12 +86,14 @@ class TabStrip : public View { DISALLOW_COPY_AND_ASSIGN(TabStrip); }; -Tab::Tab(TabbedPane* tabbed_pane, const string16& title, View* contents) +Tab::Tab(TabbedPane* tabbed_pane, const base::string16& title, View* contents) : tabbed_pane_(tabbed_pane), - title_(new Label(title, gfx::Font().DeriveFont(0, gfx::Font::BOLD))), + title_(new Label(title, + ui::ResourceBundle::GetSharedInstance().GetFontList( + ui::ResourceBundle::BoldFont))), tab_state_(TAB_ACTIVE), contents_(contents) { - // Calculate this now while the font is guaranteed to be bold. + // Calculate this now while the font list is guaranteed to be bold. preferred_title_size_ = title_->GetPreferredSize(); SetState(TAB_INACTIVE); @@ -136,7 +139,7 @@ void Tab::OnGestureEvent(ui::GestureEvent* event) { event->SetHandled(); } -gfx::Size Tab::GetPreferredSize() { +gfx::Size Tab::GetPreferredSize() const { gfx::Size size(preferred_title_size_); size.Enlarge(21, 9); const int kTabMinWidth = 54; @@ -157,18 +160,19 @@ void Tab::SetState(TabState tab_state) { return; tab_state_ = tab_state; + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); switch (tab_state) { case TAB_INACTIVE: title_->SetEnabledColor(kTabTitleColor_Inactive); - title_->SetFont(gfx::Font()); + title_->SetFontList(rb.GetFontList(ui::ResourceBundle::BaseFont)); break; case TAB_ACTIVE: title_->SetEnabledColor(kTabTitleColor_Active); - title_->SetFont(gfx::Font().DeriveFont(0, gfx::Font::BOLD)); + title_->SetFontList(rb.GetFontList(ui::ResourceBundle::BoldFont)); break; case TAB_HOVERED: title_->SetEnabledColor(kTabTitleColor_Hovered); - title_->SetFont(gfx::Font()); + title_->SetFontList(rb.GetFontList(ui::ResourceBundle::BaseFont)); break; } SchedulePaint(); @@ -178,7 +182,7 @@ TabStrip::TabStrip(TabbedPane* tabbed_pane) : tabbed_pane_(tabbed_pane) {} TabStrip::~TabStrip() {} -gfx::Size TabStrip::GetPreferredSize() { +gfx::Size TabStrip::GetPreferredSize() const { gfx::Size size; for (int i = 0; i < child_count(); ++i) { const gfx::Size child_size = child_at(i)->GetPreferredSize(); @@ -255,12 +259,12 @@ View* TabbedPane::GetSelectedTab() { NULL : GetTabAt(selected_tab_index())->contents(); } -void TabbedPane::AddTab(const string16& title, View* contents) { +void TabbedPane::AddTab(const base::string16& title, View* contents) { AddTabAtIndex(tab_strip_->child_count(), title, contents); } void TabbedPane::AddTabAtIndex(int index, - const string16& title, + const base::string16& title, View* contents) { DCHECK(index >= 0 && index <= GetTabCount()); contents->SetVisible(false); @@ -304,7 +308,7 @@ void TabbedPane::SelectTab(Tab* tab) { SelectTabAt(index); } -gfx::Size TabbedPane::GetPreferredSize() { +gfx::Size TabbedPane::GetPreferredSize() const { gfx::Size size; for (int i = 0; i < contents_->child_count(); ++i) size.SetToMax(contents_->child_at(i)->GetPreferredSize()); @@ -360,12 +364,12 @@ void TabbedPane::OnFocus() { View* selected_tab = GetSelectedTab(); if (selected_tab) { selected_tab->NotifyAccessibilityEvent( - ui::AccessibilityTypes::EVENT_FOCUS, true); + ui::AX_EVENT_FOCUS, true); } } -void TabbedPane::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_PAGETABLIST; +void TabbedPane::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_TAB_LIST; } } // namespace views diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h index a837464a120..83c7fefb058 100644 --- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h +++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h @@ -40,12 +40,12 @@ class VIEWS_EXPORT TabbedPane : public View { // Adds a new tab at the end of this TabbedPane with the specified |title|. // |contents| is the view displayed when the tab is selected and is owned by // the TabbedPane. - void AddTab(const string16& title, View* contents); + void AddTab(const base::string16& title, View* contents); // Adds a new tab at |index| with |title|. |contents| is the view displayed // when the tab is selected and is owned by the TabbedPane. If the tabbed pane // is currently empty, the new tab is selected. - void AddTabAtIndex(int index, const string16& title, View* contents); + void AddTabAtIndex(int index, const base::string16& title, View* contents); // Selects the tab at |index|, which must be valid. void SelectTabAt(int index); @@ -54,7 +54,7 @@ class VIEWS_EXPORT TabbedPane : public View { void SelectTab(Tab* tab); // Overridden from View: - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual const char* GetClassName() const OVERRIDE; private: @@ -69,7 +69,7 @@ class VIEWS_EXPORT TabbedPane : public View { const ViewHierarchyChangedDetails& details) OVERRIDE; virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE; virtual void OnFocus() OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; // A listener notified when tab selection changes. Weak, not owned. TabbedPaneListener* listener_; diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane_listener.h b/chromium/ui/views/controls/tabbed_pane/tabbed_pane_listener.h index d2607889ef1..83376f79df0 100644 --- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane_listener.h +++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane_listener.h @@ -5,11 +5,13 @@ #ifndef UI_VIEWS_CONTROLS_TABBED_PANE_TABBED_PANE_LISTENER_H_ #define UI_VIEWS_CONTROLS_TABBED_PANE_TABBED_PANE_LISTENER_H_ +#include "ui/views/views_export.h" + namespace views { // An interface implemented by an object to let it know that a tabbed pane was // selected by the user at the specified index. -class TabbedPaneListener { +class VIEWS_EXPORT TabbedPaneListener { public: // Called when the tab at |index| is selected by the user. virtual void TabSelectedAt(int index) = 0; diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc b/chromium/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc index f887a8d2ff4..f81c2a870cb 100644 --- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc +++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc @@ -9,6 +9,8 @@ #include "ui/views/controls/tabbed_pane/tabbed_pane.h" #include "ui/views/test/views_test_base.h" +using base::ASCIIToUTF16; + namespace views { namespace { @@ -20,7 +22,7 @@ class FixedSizeView : public View { : size_(size) {} // Overridden from View: - virtual gfx::Size GetPreferredSize() OVERRIDE { + virtual gfx::Size GetPreferredSize() const OVERRIDE { return size_; } diff --git a/chromium/ui/views/controls/table/table_grouper.h b/chromium/ui/views/controls/table/table_grouper.h index d6659ac1956..ce100e4c98a 100644 --- a/chromium/ui/views/controls/table/table_grouper.h +++ b/chromium/ui/views/controls/table/table_grouper.h @@ -5,9 +5,11 @@ #ifndef UI_VIEWS_CONTROLS_TABLE_TABLE_GROUPER_H_ #define UI_VIEWS_CONTROLS_TABLE_TABLE_GROUPER_H_ +#include "ui/views/views_export.h" + namespace views { -struct GroupRange { +struct VIEWS_EXPORT GroupRange { int start; int length; }; @@ -15,7 +17,7 @@ struct GroupRange { // TableGrouper is used by TableView to group a set of rows and treat them // as one. Rows that fall in the same group are selected together and sorted // together. -class TableGrouper { +class VIEWS_EXPORT TableGrouper { public: virtual void GetGroupRange(int model_index, GroupRange* range) = 0; diff --git a/chromium/ui/views/controls/table/table_header.cc b/chromium/ui/views/controls/table/table_header.cc index 16d28e3e010..a9d730f958e 100644 --- a/chromium/ui/views/controls/table/table_header.cc +++ b/chromium/ui/views/controls/table/table_header.cc @@ -5,15 +5,14 @@ #include "ui/views/controls/table/table_header.h" #include "third_party/skia/include/core/SkColor.h" +#include "ui/base/cursor/cursor.h" #include "ui/gfx/canvas.h" +#include "ui/gfx/text_utils.h" #include "ui/native_theme/native_theme.h" #include "ui/views/background.h" #include "ui/views/controls/table/table_utils.h" #include "ui/views/controls/table/table_view.h" - -#if defined(USE_AURA) -#include "ui/base/cursor/cursor.h" -#endif +#include "ui/views/native_cursor.h" namespace views { @@ -38,15 +37,6 @@ const SkColor kSeparatorColor = SkColorSetRGB(0xAA, 0xAA, 0xAA); // Size of the sort indicator (doesn't include padding). const int kSortIndicatorSize = 8; -gfx::NativeCursor GetResizeCursor() { -#if defined(USE_AURA) - return ui::kCursorColumnResize; -#elif defined(OS_WIN) - static HCURSOR g_hand_cursor = LoadCursor(NULL, IDC_SIZEWE); - return g_hand_cursor; -#endif -} - } // namespace // static @@ -95,7 +85,8 @@ void TableHeader::OnPaint(gfx::Canvas* canvas) { if (width <= 0) continue; - const int title_width = font_.GetStringWidth(columns[i].column.title); + const int title_width = + gfx::GetStringWidth(columns[i].column.title, font_list_); const bool paint_sort_indicator = (columns[i].column.id == sorted_column_id && title_width + kSortIndicatorWidth <= width); @@ -105,10 +96,10 @@ void TableHeader::OnPaint(gfx::Canvas* canvas) { width -= kSortIndicatorWidth; } - canvas->DrawStringInt( - columns[i].column.title, font_, kTextColor, - GetMirroredXWithWidthInView(x, width), kVerticalPadding, width, - height() - kVerticalPadding * 2, + canvas->DrawStringRectWithFlags( + columns[i].column.title, font_list_, kTextColor, + gfx::Rect(GetMirroredXWithWidthInView(x, width), kVerticalPadding, + width, height() - kVerticalPadding * 2), TableColumnAlignmentToCanvasAlignment(columns[i].column.alignment)); if (paint_sort_indicator) { @@ -168,13 +159,13 @@ void TableHeader::OnPaint(gfx::Canvas* canvas) { } } -gfx::Size TableHeader::GetPreferredSize() { - return gfx::Size(1, kVerticalPadding * 2 + font_.GetHeight()); +gfx::Size TableHeader::GetPreferredSize() const { + return gfx::Size(1, kVerticalPadding * 2 + font_list_.GetHeight()); } gfx::NativeCursor TableHeader::GetCursor(const ui::MouseEvent& event) { return GetResizeColumn(GetMirroredXInView(event.x())) != -1 ? - GetResizeCursor() : View::GetCursor(event); + GetNativeColumnResizeCursor() : View::GetCursor(event); } bool TableHeader::OnMousePressed(const ui::MouseEvent& event) { diff --git a/chromium/ui/views/controls/table/table_header.h b/chromium/ui/views/controls/table/table_header.h index 2aa280d2d92..a595de949c6 100644 --- a/chromium/ui/views/controls/table/table_header.h +++ b/chromium/ui/views/controls/table/table_header.h @@ -5,7 +5,7 @@ #ifndef UI_VIEWS_CONTROLS_TABLE_TABLE_HEADER_H_ #define UI_VIEWS_CONTROLS_TABLE_TABLE_HEADER_H_ -#include "ui/gfx/font.h" +#include "ui/gfx/font_list.h" #include "ui/views/view.h" #include "ui/views/views_export.h" @@ -25,12 +25,12 @@ class VIEWS_EXPORT TableHeader : public views::View { explicit TableHeader(TableView* table); virtual ~TableHeader(); - const gfx::Font& font() const { return font_; } + const gfx::FontList& font_list() const { return font_list_; } // views::View overrides. virtual void Layout() OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual gfx::NativeCursor GetCursor(const ui::MouseEvent& event) OVERRIDE; virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE; virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE; @@ -70,7 +70,7 @@ class VIEWS_EXPORT TableHeader : public views::View { bool is_resizing() const { return resize_details_.get() != NULL; } - const gfx::Font font_; + const gfx::FontList font_list_; TableView* table_; diff --git a/chromium/ui/views/controls/table/table_utils.cc b/chromium/ui/views/controls/table/table_utils.cc index 1e730d0a938..393c22dbc6f 100644 --- a/chromium/ui/views/controls/table/table_utils.cc +++ b/chromium/ui/views/controls/table/table_utils.cc @@ -6,26 +6,28 @@ #include "base/logging.h" #include "ui/gfx/canvas.h" -#include "ui/gfx/font.h" +#include "ui/gfx/font_list.h" +#include "ui/gfx/text_utils.h" #include "ui/views/controls/table/table_view.h" namespace views { const int kUnspecifiedColumnWidth = 90; -int WidthForContent(const gfx::Font& header_font, - const gfx::Font& content_font, +int WidthForContent(const gfx::FontList& header_font_list, + const gfx::FontList& content_font_list, int padding, int header_padding, const ui::TableColumn& column, ui::TableModel* model) { int width = header_padding; if (!column.title.empty()) - width = header_font.GetStringWidth(column.title) + header_padding; + width = gfx::GetStringWidth(column.title, header_font_list) + + header_padding; for (int i = 0, row_count = model->RowCount(); i < row_count; ++i) { const int cell_width = - content_font.GetStringWidth(model->GetText(i, column.id)); + gfx::GetStringWidth(model->GetText(i, column.id), content_font_list); width = std::max(width, cell_width); } return width + padding; @@ -34,8 +36,8 @@ int WidthForContent(const gfx::Font& header_font, std::vector<int> CalculateTableColumnSizes( int width, int first_column_padding, - const gfx::Font& header_font, - const gfx::Font& content_font, + const gfx::FontList& header_font_list, + const gfx::FontList& content_font_list, int padding, int header_padding, const std::vector<ui::TableColumn>& columns, @@ -49,11 +51,12 @@ std::vector<int> CalculateTableColumnSizes( if (column.percent > 0) { total_percent += column.percent; // Make sure there is at least enough room for the header. - content_widths[i] = header_font.GetStringWidth(column.title) + padding + - header_padding; + content_widths[i] = gfx::GetStringWidth(column.title, header_font_list) + + padding + header_padding; } else { - content_widths[i] = WidthForContent(header_font, content_font, padding, - header_padding, column, model); + content_widths[i] = WidthForContent(header_font_list, content_font_list, + padding, header_padding, column, + model); if (i == 0) content_widths[i] += first_column_padding; } diff --git a/chromium/ui/views/controls/table/table_utils.h b/chromium/ui/views/controls/table/table_utils.h index 3c5d4c78175..ec387102e69 100644 --- a/chromium/ui/views/controls/table/table_utils.h +++ b/chromium/ui/views/controls/table/table_utils.h @@ -11,7 +11,7 @@ #include "ui/views/views_export.h" namespace gfx { -class Font; +class FontList; } namespace views { @@ -23,23 +23,23 @@ VIEWS_EXPORT extern const int kUnspecifiedColumnWidth; // Returns the width needed to display the contents of the specified column. // This is used internally by CalculateTableColumnSizes() and generally not // useful by itself. |header_padding| is padding added to the header. -VIEWS_EXPORT int WidthForContent(const gfx::Font& header_font, - const gfx::Font& content_font, +VIEWS_EXPORT int WidthForContent(const gfx::FontList& header_font_list, + const gfx::FontList& content_font_list, int padding, int header_padding, const ui::TableColumn& column, ui::TableModel* model); // Determines the width for each of the specified columns. |width| is the width -// to fit the columns into. |header_font| the font used to draw the header and -// |content_font| the header used to draw the content. |padding| is extra -// horizontal spaced added to each cell, and |header_padding| added to the -// width needed for the header. +// to fit the columns into. |header_font_list| the font list used to draw the +// header and |content_font_list| the header used to draw the content. |padding| +// is extra horizontal spaced added to each cell, and |header_padding| added to +// the width needed for the header. VIEWS_EXPORT std::vector<int> CalculateTableColumnSizes( int width, int first_column_padding, - const gfx::Font& header_font, - const gfx::Font& content_font, + const gfx::FontList& header_font_list, + const gfx::FontList& content_font_list, int padding, int header_padding, const std::vector<ui::TableColumn>& columns, diff --git a/chromium/ui/views/controls/table/table_utils_unittest.cc b/chromium/ui/views/controls/table/table_utils_unittest.cc index e6c6369d0b8..98b1fd2f786 100644 --- a/chromium/ui/views/controls/table/table_utils_unittest.cc +++ b/chromium/ui/views/controls/table/table_utils_unittest.cc @@ -6,7 +6,7 @@ #include "base/strings/string_number_conversions.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/font.h" +#include "ui/gfx/font_list.h" #include "ui/views/controls/table/test_table_model.h" using ui::TableColumn; @@ -40,17 +40,19 @@ TEST(TableUtilsTest, SetWidthHonored) { std::vector<TableColumn> columns; columns.push_back(CreateTableColumnWithWidth(20)); columns.push_back(CreateTableColumnWithWidth(30)); - gfx::Font font; - std::vector<int> result( - CalculateTableColumnSizes(100, 0, font, font, 0, 0, columns, &model)); + gfx::FontList font_list; + std::vector<int> result(CalculateTableColumnSizes( + 100, 0, font_list, font_list, 0, 0, columns, &model)); EXPECT_EQ("20,30", IntVectorToString(result)); // Same with some padding, it should be ignored. - result = CalculateTableColumnSizes(100, 0, font, font, 2, 0, columns, &model); + result = CalculateTableColumnSizes( + 100, 0, font_list, font_list, 2, 0, columns, &model); EXPECT_EQ("20,30", IntVectorToString(result)); // Same with not enough space, it shouldn't matter. - result = CalculateTableColumnSizes(10, 0, font, font, 2, 0, columns, &model); + result = CalculateTableColumnSizes( + 10, 0, font_list, font_list, 2, 0, columns, &model); EXPECT_EQ("20,30", IntVectorToString(result)); } @@ -61,11 +63,12 @@ TEST(TableUtilsTest, LastColumnGetsAllSpace) { std::vector<TableColumn> columns; columns.push_back(ui::TableColumn()); columns.push_back(ui::TableColumn()); - gfx::Font font; - std::vector<int> result( - CalculateTableColumnSizes(500, 0, font, font, 0, 0, columns, &model)); + gfx::FontList font_list; + std::vector<int> result(CalculateTableColumnSizes( + 500, 0, font_list, font_list, 0, 0, columns, &model)); EXPECT_NE(0, result[0]); - EXPECT_GE(result[1], WidthForContent(font, font, 0, 0, columns[1], &model)); + EXPECT_GE(result[1], + WidthForContent(font_list, font_list, 0, 0, columns[1], &model)); EXPECT_EQ(500, result[0] + result[1]); } @@ -77,38 +80,45 @@ TEST(TableUtilsTest, SingleResizableColumn) { columns.push_back(ui::TableColumn()); columns.push_back(ui::TableColumn()); columns[2].percent = 1.0f; - gfx::Font font; - std::vector<int> result( - CalculateTableColumnSizes(500, 0, font, font, 0, 0, columns, &model)); - EXPECT_EQ(result[0], WidthForContent(font, font, 0, 0, columns[0], &model)); - EXPECT_EQ(result[1], WidthForContent(font, font, 0, 0, columns[1], &model)); + gfx::FontList font_list; + std::vector<int> result(CalculateTableColumnSizes( + 500, 0, font_list, font_list, 0, 0, columns, &model)); + EXPECT_EQ(result[0], + WidthForContent(font_list, font_list, 0, 0, columns[0], &model)); + EXPECT_EQ(result[1], + WidthForContent(font_list, font_list, 0, 0, columns[1], &model)); EXPECT_EQ(500 - result[0] - result[1], result[2]); // The same with a slightly larger width passed in. - result = - CalculateTableColumnSizes(1000, 0, font, font, 0, 0, columns, &model); - EXPECT_EQ(result[0], WidthForContent(font, font, 0, 0, columns[0], &model)); - EXPECT_EQ(result[1], WidthForContent(font, font, 0, 0, columns[1], &model)); + result = CalculateTableColumnSizes( + 1000, 0, font_list, font_list, 0, 0, columns, &model); + EXPECT_EQ(result[0], + WidthForContent(font_list, font_list, 0, 0, columns[0], &model)); + EXPECT_EQ(result[1], + WidthForContent(font_list, font_list, 0, 0, columns[1], &model)); EXPECT_EQ(1000 - result[0] - result[1], result[2]); // Verify padding for the first column is honored. - result = - CalculateTableColumnSizes(1000, 10, font, font, 0, 0, columns, &model); + result = CalculateTableColumnSizes( + 1000, 10, font_list, font_list, 0, 0, columns, &model); EXPECT_EQ(result[0], - WidthForContent(font, font, 0, 0, columns[0], &model) + 10); - EXPECT_EQ(result[1], WidthForContent(font, font, 0, 0, columns[1], &model)); + WidthForContent(font_list, font_list, 0, 0, columns[0], &model) + + 10); + EXPECT_EQ(result[1], + WidthForContent(font_list, font_list, 0, 0, columns[1], &model)); EXPECT_EQ(1000 - result[0] - result[1], result[2]); // Just enough space to show the first two columns. Should force last column // to min size. - result = - CalculateTableColumnSizes(1000, 0, font, font, 0, 0, columns, &model); - result = CalculateTableColumnSizes(result[0] + result[1], 0, font, font, 0, 0, - columns, &model); - EXPECT_EQ(result[0], WidthForContent(font, font, 0, 0, columns[0], &model)); - EXPECT_EQ(result[1], WidthForContent(font, font, 0, 0, columns[1], &model)); + result = CalculateTableColumnSizes( + 1000, 0, font_list, font_list, 0, 0, columns, &model); + result = CalculateTableColumnSizes( + result[0] + result[1], 0, font_list, font_list, 0, 0, columns, &model); + EXPECT_EQ(result[0], + WidthForContent(font_list, font_list, 0, 0, columns[0], &model)); + EXPECT_EQ(result[1], + WidthForContent(font_list, font_list, 0, 0, columns[1], &model)); EXPECT_EQ(kUnspecifiedColumnWidth, result[2]); } } // namespace views - diff --git a/chromium/ui/views/controls/table/table_view.cc b/chromium/ui/views/controls/table/table_view.cc index 8a27a1f7a91..e5120d49295 100644 --- a/chromium/ui/views/controls/table/table_view.cc +++ b/chromium/ui/views/controls/table/table_view.cc @@ -13,6 +13,7 @@ #include "ui/gfx/image/image_skia.h" #include "ui/gfx/rect_conversions.h" #include "ui/gfx/skia_util.h" +#include "ui/gfx/text_utils.h" #include "ui/native_theme/native_theme.h" #include "ui/views/controls/scroll_view.h" #include "ui/views/controls/table/table_grouper.h" @@ -124,7 +125,7 @@ TableView::TableView(ui::TableModel* model, table_type_(table_type), single_selection_(single_selection), table_view_observer_(NULL), - row_height_(font_.GetHeight() + kTextVerticalPadding * 2), + row_height_(font_list_.GetHeight() + kTextVerticalPadding * 2), last_parent_width_(0), layout_width_(0), grouper_(NULL), @@ -317,7 +318,7 @@ void TableView::Layout() { SetBounds(x(), y(), width, height); } -gfx::Size TableView::GetPreferredSize() { +gfx::Size TableView::GetPreferredSize() const { int width = 50; if (header_ && !visible_columns_.empty()) width = visible_columns_.back().x + visible_columns_.back().width; @@ -404,7 +405,7 @@ void TableView::OnGestureEvent(ui::GestureEvent* event) { } bool TableView::GetTooltipText(const gfx::Point& p, - string16* tooltip) const { + base::string16* tooltip) const { return GetTooltipImpl(p, tooltip, NULL); } @@ -522,14 +523,14 @@ void TableView::OnPaint(gfx::Canvas* canvas) { text_x += kImageSize + kTextHorizontalPadding; } if (text_x < cell_bounds.right() - kTextHorizontalPadding) { - canvas->DrawStringInt( - model_->GetText(model_index, visible_columns_[j].column.id), font_, - is_selected ? selected_fg_color : fg_color, - GetMirroredXWithWidthInView(text_x, cell_bounds.right() - text_x - - kTextHorizontalPadding), - cell_bounds.y() + kTextVerticalPadding, - cell_bounds.right() - text_x, - cell_bounds.height() - kTextVerticalPadding * 2, + canvas->DrawStringRectWithFlags( + model_->GetText(model_index, visible_columns_[j].column.id), + font_list_, is_selected ? selected_fg_color : fg_color, + gfx::Rect(GetMirroredXWithWidthInView( + text_x, cell_bounds.right() - text_x - kTextHorizontalPadding), + cell_bounds.y() + kTextVerticalPadding, + cell_bounds.right() - text_x, + cell_bounds.height() - kTextVerticalPadding * 2), TableColumnAlignmentToCanvasAlignment( visible_columns_[j].column.alignment)); } @@ -684,7 +685,7 @@ void TableView::UpdateVisibleColumnSizes() { first_column_padding += kGroupingIndicatorSize + kTextHorizontalPadding; std::vector<int> sizes = views::CalculateTableColumnSizes( - layout_width_, first_column_padding, header_->font(), font_, + layout_width_, first_column_padding, header_->font_list(), font_list_, std::max(kTextHorizontalPadding, TableHeader::kHorizontalPadding) * 2, TableHeader::kSortIndicatorWidth, columns, model_); DCHECK_EQ(visible_columns_.size(), sizes.size()); @@ -871,7 +872,7 @@ GroupRange TableView::GetGroupRange(int model_index) const { } bool TableView::GetTooltipImpl(const gfx::Point& location, - string16* tooltip, + base::string16* tooltip, gfx::Point* tooltip_origin) const { const int row = location.y() / row_height_; if (row < 0 || row >= RowCount() || visible_columns_.empty()) @@ -883,7 +884,7 @@ bool TableView::GetTooltipImpl(const gfx::Point& location, x > (visible_columns_[column].x + visible_columns_[column].width)) return false; - const string16 text(model_->GetText(ViewToModel(row), + const base::string16 text(model_->GetText(ViewToModel(row), visible_columns_[column].column.id)); if (text.empty()) return false; @@ -892,7 +893,7 @@ bool TableView::GetTooltipImpl(const gfx::Point& location, AdjustCellBoundsForText(column, &cell_bounds); const int right = std::min(GetVisibleBounds().right(), cell_bounds.right()); if (right > cell_bounds.x() && - font_.GetStringWidth(text) <= (right - cell_bounds.x())) + gfx::GetStringWidth(text, font_list_) <= (right - cell_bounds.x())) return false; if (tooltip) diff --git a/chromium/ui/views/controls/table/table_view.h b/chromium/ui/views/controls/table/table_view.h index c2a94724a52..96268067c6b 100644 --- a/chromium/ui/views/controls/table/table_view.h +++ b/chromium/ui/views/controls/table/table_view.h @@ -11,7 +11,7 @@ #include "ui/base/models/list_selection_model.h" #include "ui/base/models/table_model.h" #include "ui/base/models/table_model_observer.h" -#include "ui/gfx/font.h" +#include "ui/gfx/font_list.h" #include "ui/views/view.h" #include "ui/views/views_export.h" @@ -166,12 +166,12 @@ class VIEWS_EXPORT TableView // View overrides: virtual void Layout() OVERRIDE; - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE; virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE; virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; virtual bool GetTooltipText(const gfx::Point& p, - string16* tooltip) const OVERRIDE; + base::string16* tooltip) const OVERRIDE; virtual bool GetTooltipTextOrigin(const gfx::Point& p, gfx::Point* loc) const OVERRIDE; @@ -291,7 +291,7 @@ class VIEWS_EXPORT TableView // sets |tooltip| and/or |tooltip_origin| as appropriate, each of which may be // NULL. bool GetTooltipImpl(const gfx::Point& location, - string16* tooltip, + base::string16* tooltip, gfx::Point* tooltip_origin) const; ui::TableModel* model_; @@ -316,7 +316,7 @@ class VIEWS_EXPORT TableView // The selection, in terms of the model. ui::ListSelectionModel selection_model_; - gfx::Font font_; + gfx::FontList font_list_; int row_height_; diff --git a/chromium/ui/views/controls/table/table_view_observer.h b/chromium/ui/views/controls/table/table_view_observer.h index 44400eee1c7..9cea2603c50 100644 --- a/chromium/ui/views/controls/table/table_view_observer.h +++ b/chromium/ui/views/controls/table/table_view_observer.h @@ -6,6 +6,7 @@ #define UI_VIEWS_CONTROLS_TABLE_TABLE_VIEW_OBSERVER_H_ #include "ui/events/keycodes/keyboard_codes.h" +#include "ui/views/views_export.h" namespace views { @@ -13,7 +14,7 @@ class TableView; class TableView2; // TableViewObserver is notified about the TableView selection. -class TableViewObserver { +class VIEWS_EXPORT TableViewObserver { public: virtual ~TableViewObserver() {} diff --git a/chromium/ui/views/controls/table/table_view_unittest.cc b/chromium/ui/views/controls/table/table_view_unittest.cc index 4d89f3272b3..1d59358d31e 100644 --- a/chromium/ui/views/controls/table/table_view_unittest.cc +++ b/chromium/ui/views/controls/table/table_view_unittest.cc @@ -67,7 +67,7 @@ class TestTableModel2 : public ui::TableModel { // ui::TableModel: virtual int RowCount() OVERRIDE; - virtual string16 GetText(int row, int column_id) OVERRIDE; + virtual base::string16 GetText(int row, int column_id) OVERRIDE; virtual void SetObserver(ui::TableModelObserver* observer) OVERRIDE; virtual int CompareValues(int row1, int row2, int column_id) OVERRIDE; @@ -115,7 +115,7 @@ int TestTableModel2::RowCount() { return static_cast<int>(rows_.size()); } -string16 TestTableModel2::GetText(int row, int column_id) { +base::string16 TestTableModel2::GetText(int row, int column_id) { return base::IntToString16(rows_[row][column_id]); } @@ -175,9 +175,9 @@ class TableViewTest : public testing::Test { virtual void SetUp() OVERRIDE { model_.reset(new TestTableModel2); std::vector<ui::TableColumn> columns(2); - columns[0].title = ASCIIToUTF16("Title Column 0"); + columns[0].title = base::ASCIIToUTF16("Title Column 0"); columns[0].sortable = true; - columns[1].title = ASCIIToUTF16("Title Column 1"); + columns[1].title = base::ASCIIToUTF16("Title Column 1"); columns[1].id = 1; columns[1].sortable = true; table_ = new TestTableView(model_.get(), columns); @@ -191,7 +191,8 @@ class TableViewTest : public testing::Test { const int y = row * table_->row_height(); const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(0, y), gfx::Point(0, y), - ui::EF_LEFT_MOUSE_BUTTON | flags); + ui::EF_LEFT_MOUSE_BUTTON | flags, + ui::EF_LEFT_MOUSE_BUTTON); table_->OnMousePressed(pressed); } @@ -289,10 +290,12 @@ TEST_F(TableViewTest, Resize) { EXPECT_NE(0, x); // Drag the mouse 1 pixel to the left. const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(x, 0), - gfx::Point(x, 0), ui::EF_LEFT_MOUSE_BUTTON); + gfx::Point(x, 0), ui::EF_LEFT_MOUSE_BUTTON, + ui::EF_LEFT_MOUSE_BUTTON); helper_->header()->OnMousePressed(pressed); const ui::MouseEvent dragged(ui::ET_MOUSE_DRAGGED, gfx::Point(x - 1, 0), - gfx::Point(x - 1, 0), ui::EF_LEFT_MOUSE_BUTTON); + gfx::Point(x - 1, 0), ui::EF_LEFT_MOUSE_BUTTON, + 0); helper_->header()->OnMouseDragged(dragged); // This should shrink the first column and pull the second column in. @@ -378,11 +381,13 @@ TEST_F(TableViewTest, SortOnMouse) { EXPECT_NE(0, x); // Press and release the mouse. const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(x, 0), - gfx::Point(x, 0), ui::EF_LEFT_MOUSE_BUTTON); + gfx::Point(x, 0), ui::EF_LEFT_MOUSE_BUTTON, + ui::EF_LEFT_MOUSE_BUTTON); // The header must return true, else it won't normally get the release. EXPECT_TRUE(helper_->header()->OnMousePressed(pressed)); const ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(x, 0), - gfx::Point(x, 0), ui::EF_LEFT_MOUSE_BUTTON); + gfx::Point(x, 0), ui::EF_LEFT_MOUSE_BUTTON, + ui::EF_LEFT_MOUSE_BUTTON); helper_->header()->OnMouseReleased(release); ASSERT_EQ(1u, table_->sort_descriptors().size()); diff --git a/chromium/ui/views/controls/table/test_table_model.cc b/chromium/ui/views/controls/table/test_table_model.cc index c855aa163ec..6cac56cff3d 100644 --- a/chromium/ui/views/controls/table/test_table_model.cc +++ b/chromium/ui/views/controls/table/test_table_model.cc @@ -22,9 +22,9 @@ int TestTableModel::RowCount() { return row_count_; } -string16 TestTableModel::GetText(int row, int column_id) { - return ASCIIToUTF16(base::IntToString(row) + "x" + - base::IntToString(column_id)); +base::string16 TestTableModel::GetText(int row, int column_id) { + return base::ASCIIToUTF16(base::IntToString(row) + "x" + + base::IntToString(column_id)); } gfx::ImageSkia TestTableModel::GetIcon(int row) { diff --git a/chromium/ui/views/controls/table/test_table_model.h b/chromium/ui/views/controls/table/test_table_model.h index d093102fbe9..8338aae78a5 100644 --- a/chromium/ui/views/controls/table/test_table_model.h +++ b/chromium/ui/views/controls/table/test_table_model.h @@ -15,7 +15,7 @@ class TestTableModel : public ui::TableModel { // ui::TableModel overrides: virtual int RowCount() OVERRIDE; - virtual string16 GetText(int row, int column_id) OVERRIDE; + virtual base::string16 GetText(int row, int column_id) OVERRIDE; virtual gfx::ImageSkia GetIcon(int row) OVERRIDE; virtual void SetObserver(ui::TableModelObserver* observer) OVERRIDE; diff --git a/chromium/ui/views/controls/textfield/native_textfield_views.cc b/chromium/ui/views/controls/textfield/native_textfield_views.cc deleted file mode 100644 index f8fc36217d2..00000000000 --- a/chromium/ui/views/controls/textfield/native_textfield_views.cc +++ /dev/null @@ -1,1545 +0,0 @@ -// 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/controls/textfield/native_textfield_views.h" - -#include <algorithm> -#include <set> - -#include "base/bind.h" -#include "base/debug/trace_event.h" -#include "base/i18n/case_conversion.h" -#include "base/logging.h" -#include "base/message_loop/message_loop.h" -#include "base/strings/utf_string_conversions.h" -#include "grit/ui_strings.h" -#include "third_party/icu/source/common/unicode/uchar.h" -#include "third_party/skia/include/core/SkColor.h" -#include "ui/base/clipboard/clipboard.h" -#include "ui/base/dragdrop/drag_drop_types.h" -#include "ui/base/dragdrop/drag_utils.h" -#include "ui/base/l10n/l10n_util.h" -#include "ui/base/ui_base_switches_util.h" -#include "ui/compositor/layer.h" -#include "ui/events/event.h" -#include "ui/gfx/canvas.h" -#include "ui/gfx/insets.h" -#include "ui/gfx/range/range.h" -#include "ui/gfx/render_text.h" -#include "ui/gfx/text_constants.h" -#include "ui/native_theme/native_theme.h" -#include "ui/views/background.h" -#include "ui/views/border.h" -#include "ui/views/controls/focusable_border.h" -#include "ui/views/controls/menu/menu_item_view.h" -#include "ui/views/controls/menu/menu_model_adapter.h" -#include "ui/views/controls/menu/menu_runner.h" -#include "ui/views/controls/textfield/textfield.h" -#include "ui/views/controls/textfield/textfield_controller.h" -#include "ui/views/controls/textfield/textfield_views_model.h" -#include "ui/views/drag_utils.h" -#include "ui/views/ime/input_method.h" -#include "ui/views/metrics.h" -#include "ui/views/widget/widget.h" - -#if defined(USE_AURA) -#include "ui/base/cursor/cursor.h" -#endif - -#if defined(OS_WIN) && defined(USE_AURA) -#include "base/win/win_util.h" -#endif - -namespace { - -void ConvertRectToScreen(const views::View* src, gfx::Rect* r) { - DCHECK(src); - - gfx::Point new_origin = r->origin(); - views::View::ConvertPointToScreen(src, &new_origin); - r->set_origin(new_origin); -} - -} // namespace - -namespace views { - -const char NativeTextfieldViews::kViewClassName[] = - "views/NativeTextfieldViews"; - -NativeTextfieldViews::NativeTextfieldViews(Textfield* parent) - : textfield_(parent), - model_(new TextfieldViewsModel(this)), - text_border_(new FocusableBorder()), - is_cursor_visible_(false), - is_drop_cursor_visible_(false), - skip_input_method_cancel_composition_(false), - initiating_drag_(false), - cursor_timer_(this), - aggregated_clicks_(0) { - set_border(text_border_); - GetRenderText()->SetFontList(textfield_->font_list()); - UpdateColorsFromTheme(GetNativeTheme()); - set_context_menu_controller(this); - parent->set_context_menu_controller(this); - set_drag_controller(this); -} - -NativeTextfieldViews::~NativeTextfieldViews() { -} - -//////////////////////////////////////////////////////////////////////////////// -// NativeTextfieldViews, View overrides: - -bool NativeTextfieldViews::OnMousePressed(const ui::MouseEvent& event) { - OnBeforeUserAction(); - TrackMouseClicks(event); - - TextfieldController* controller = textfield_->GetController(); - if (!(controller && controller->HandleMouseEvent(textfield_, event)) && - !textfield_->OnMousePressed(event)) { - HandleMousePressEvent(event); - } - - OnAfterUserAction(); - touch_selection_controller_.reset(); - return true; -} - -bool NativeTextfieldViews::ExceededDragThresholdFromLastClickLocation( - const ui::MouseEvent& event) { - return ExceededDragThreshold(event.location() - last_click_location_); -} - -bool NativeTextfieldViews::OnMouseDragged(const ui::MouseEvent& event) { - // Don't adjust the cursor on a potential drag and drop, or if the mouse - // movement from the last mouse click does not exceed the drag threshold. - if (initiating_drag_ || !ExceededDragThresholdFromLastClickLocation(event) || - !event.IsOnlyLeftMouseButton()) { - return true; - } - - OnBeforeUserAction(); - // TODO: Remove once NativeTextfield implementations are consolidated to - // Textfield. - if (!textfield_->OnMouseDragged(event)) { - MoveCursorTo(event.location(), true); - if (aggregated_clicks_ == 1) { - model_->SelectWord(); - // Expand the selection so the initially selected word remains selected. - gfx::Range selection = GetRenderText()->selection(); - const size_t min = std::min(selection.GetMin(), - double_click_word_.GetMin()); - const size_t max = std::max(selection.GetMax(), - double_click_word_.GetMax()); - const bool reversed = selection.is_reversed(); - selection.set_start(reversed ? max : min); - selection.set_end(reversed ? min : max); - model_->SelectRange(selection); - } - SchedulePaint(); - } - OnAfterUserAction(); - return true; -} - -void NativeTextfieldViews::OnMouseReleased(const ui::MouseEvent& event) { - OnBeforeUserAction(); - // TODO: Remove once NativeTextfield implementations are consolidated to - // Textfield. - textfield_->OnMouseReleased(event); - // Cancel suspected drag initiations, the user was clicking in the selection. - if (initiating_drag_ && MoveCursorTo(event.location(), false)) - SchedulePaint(); - initiating_drag_ = false; - OnAfterUserAction(); -} - -void NativeTextfieldViews::OnGestureEvent(ui::GestureEvent* event) { - textfield_->OnGestureEvent(event); - if (event->handled()) - return; - - switch (event->type()) { - case ui::ET_GESTURE_TAP_DOWN: - OnBeforeUserAction(); - textfield_->RequestFocus(); - // We don't deselect if the point is in the selection - // because TAP_DOWN may turn into a LONG_PRESS. - if (!GetRenderText()->IsPointInSelection(event->location()) && - MoveCursorTo(event->location(), false)) - SchedulePaint(); - OnAfterUserAction(); - event->SetHandled(); - break; - case ui::ET_GESTURE_SCROLL_UPDATE: - OnBeforeUserAction(); - if (MoveCursorTo(event->location(), true)) - SchedulePaint(); - OnAfterUserAction(); - event->SetHandled(); - break; - case ui::ET_GESTURE_SCROLL_END: - case ui::ET_SCROLL_FLING_START: - CreateTouchSelectionControllerAndNotifyIt(); - event->SetHandled(); - break; - case ui::ET_GESTURE_TAP: - if (event->details().tap_count() == 1) { - CreateTouchSelectionControllerAndNotifyIt(); - } else { - OnBeforeUserAction(); - SelectAll(false); - OnAfterUserAction(); - event->SetHandled(); - } - break; - case ui::ET_GESTURE_LONG_PRESS: - // If long press happens outside selection, select word and show context - // menu (If touch selection is enabled, context menu is shown by the - // |touch_selection_controller_|, hence we mark the event handled. - // Otherwise, the regular context menu will be shown by views). - // If long press happens in selected text and touch drag drop is enabled, - // we will turn off touch selection (if one exists) and let views do drag - // drop. - if (!GetRenderText()->IsPointInSelection(event->location())) { - OnBeforeUserAction(); - model_->SelectWord(); - touch_selection_controller_.reset( - ui::TouchSelectionController::create(this)); - OnCaretBoundsChanged(); - SchedulePaint(); - OnAfterUserAction(); - if (touch_selection_controller_.get()) - event->SetHandled(); - } else if (switches::IsTouchDragDropEnabled()) { - initiating_drag_ = true; - touch_selection_controller_.reset(); - } else { - if (!touch_selection_controller_.get()) - CreateTouchSelectionControllerAndNotifyIt(); - if (touch_selection_controller_.get()) - event->SetHandled(); - } - return; - case ui::ET_GESTURE_LONG_TAP: - if (!touch_selection_controller_.get()) - CreateTouchSelectionControllerAndNotifyIt(); - - // If touch selection is enabled, the context menu on long tap will be - // shown by the |touch_selection_controller_|, hence we mark the event - // handled so views does not try to show context menu on it. - if (touch_selection_controller_.get()) - event->SetHandled(); - break; - default: - View::OnGestureEvent(event); - return; - } - PlatformGestureEventHandling(event); -} - -bool NativeTextfieldViews::OnKeyPressed(const ui::KeyEvent& event) { - // OnKeyPressed/OnKeyReleased/OnFocus/OnBlur will never be invoked on - // NativeTextfieldViews as it will never gain focus. - NOTREACHED(); - return false; -} - -bool NativeTextfieldViews::OnKeyReleased(const ui::KeyEvent& event) { - NOTREACHED(); - return false; -} - -bool NativeTextfieldViews::GetDropFormats( - int* formats, - std::set<OSExchangeData::CustomFormat>* custom_formats) { - if (!textfield_->enabled() || textfield_->read_only()) - return false; - // TODO(msw): Can we support URL, FILENAME, etc.? - *formats = ui::OSExchangeData::STRING; - TextfieldController* controller = textfield_->GetController(); - if (controller) - controller->AppendDropFormats(formats, custom_formats); - return true; -} - -bool NativeTextfieldViews::CanDrop(const OSExchangeData& data) { - int formats; - std::set<OSExchangeData::CustomFormat> custom_formats; - GetDropFormats(&formats, &custom_formats); - return textfield_->enabled() && !textfield_->read_only() && - data.HasAnyFormat(formats, custom_formats); -} - -int NativeTextfieldViews::OnDragUpdated(const ui::DropTargetEvent& event) { - DCHECK(CanDrop(event.data())); - - const gfx::Range& selection = GetRenderText()->selection(); - drop_cursor_position_ = GetRenderText()->FindCursorPosition(event.location()); - bool in_selection = !selection.is_empty() && - selection.Contains(gfx::Range(drop_cursor_position_.caret_pos())); - is_drop_cursor_visible_ = !in_selection; - // TODO(msw): Pan over text when the user drags to the visible text edge. - OnCaretBoundsChanged(); - SchedulePaint(); - - if (initiating_drag_) { - if (in_selection) - return ui::DragDropTypes::DRAG_NONE; - return event.IsControlDown() ? ui::DragDropTypes::DRAG_COPY : - ui::DragDropTypes::DRAG_MOVE; - } - return ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE; -} - -void NativeTextfieldViews::OnDragExited() { - is_drop_cursor_visible_ = false; - SchedulePaint(); -} - -int NativeTextfieldViews::OnPerformDrop(const ui::DropTargetEvent& event) { - DCHECK(CanDrop(event.data())); - - is_drop_cursor_visible_ = false; - - TextfieldController* controller = textfield_->GetController(); - if (controller) { - int drag_operation = controller->OnDrop(event.data()); - if (drag_operation != ui::DragDropTypes::DRAG_NONE) - return drag_operation; - } - - DCHECK(!initiating_drag_ || - !GetRenderText()->IsPointInSelection(event.location())); - OnBeforeUserAction(); - skip_input_method_cancel_composition_ = true; - - gfx::SelectionModel drop_destination_model = - GetRenderText()->FindCursorPosition(event.location()); - string16 text; - event.data().GetString(&text); - text = GetTextForDisplay(text); - - // Delete the current selection for a drag and drop within this view. - const bool move = initiating_drag_ && !event.IsControlDown() && - event.source_operations() & ui::DragDropTypes::DRAG_MOVE; - if (move) { - // Adjust the drop destination if it is on or after the current selection. - size_t drop = drop_destination_model.caret_pos(); - drop -= GetSelectedRange().Intersect(gfx::Range(0, drop)).length(); - model_->DeleteSelectionAndInsertTextAt(text, drop); - } else { - model_->MoveCursorTo(drop_destination_model); - // Drop always inserts text even if the textfield is not in insert mode. - model_->InsertText(text); - } - skip_input_method_cancel_composition_ = false; - UpdateAfterChange(true, true); - OnAfterUserAction(); - return move ? ui::DragDropTypes::DRAG_MOVE : ui::DragDropTypes::DRAG_COPY; -} - -void NativeTextfieldViews::OnDragDone() { - initiating_drag_ = false; - is_drop_cursor_visible_ = false; -} - -void NativeTextfieldViews::OnPaint(gfx::Canvas* canvas) { - OnPaintBackground(canvas); - PaintTextAndCursor(canvas); - if (textfield_->draw_border()) - OnPaintBorder(canvas); -} - -void NativeTextfieldViews::OnFocus() { - NOTREACHED(); -} - -void NativeTextfieldViews::OnBlur() { - NOTREACHED(); -} - -void NativeTextfieldViews::OnNativeThemeChanged(const ui::NativeTheme* theme) { - UpdateColorsFromTheme(theme); -} - -void NativeTextfieldViews::SelectRect(const gfx::Point& start, - const gfx::Point& end) { - if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) - return; - - gfx::SelectionModel start_caret = GetRenderText()->FindCursorPosition(start); - gfx::SelectionModel end_caret = GetRenderText()->FindCursorPosition(end); - gfx::SelectionModel selection( - gfx::Range(start_caret.caret_pos(), end_caret.caret_pos()), - end_caret.caret_affinity()); - - OnBeforeUserAction(); - model_->SelectSelectionModel(selection); - OnCaretBoundsChanged(); - SchedulePaint(); - OnAfterUserAction(); -} - -void NativeTextfieldViews::MoveCaretTo(const gfx::Point& point) { - SelectRect(point, point); -} - -void NativeTextfieldViews::GetSelectionEndPoints(gfx::Rect* p1, - gfx::Rect* p2) { - gfx::RenderText* render_text = GetRenderText(); - const gfx::SelectionModel& sel = render_text->selection_model(); - gfx::SelectionModel start_sel = - render_text->GetSelectionModelForSelectionStart(); - *p1 = render_text->GetCursorBounds(start_sel, true); - *p2 = render_text->GetCursorBounds(sel, true); -} - -gfx::Rect NativeTextfieldViews::GetBounds() { - return bounds(); -} - -gfx::NativeView NativeTextfieldViews::GetNativeView() { - return GetWidget()->GetNativeView(); -} - -void NativeTextfieldViews::ConvertPointToScreen(gfx::Point* point) { - View::ConvertPointToScreen(this, point); -} - -void NativeTextfieldViews::ConvertPointFromScreen(gfx::Point* point) { - View::ConvertPointFromScreen(this, point); -} - -bool NativeTextfieldViews::DrawsHandles() { - return false; -} - -void NativeTextfieldViews::OpenContextMenu(const gfx::Point& anchor) { - touch_selection_controller_.reset(); - ShowContextMenu(anchor, ui::MENU_SOURCE_TOUCH_EDIT_MENU); -} - -gfx::NativeCursor NativeTextfieldViews::GetCursor(const ui::MouseEvent& event) { - bool in_selection = GetRenderText()->IsPointInSelection(event.location()); - bool drag_event = event.type() == ui::ET_MOUSE_DRAGGED; - bool text_cursor = !initiating_drag_ && (drag_event || !in_selection); -#if defined(USE_AURA) - return text_cursor ? ui::kCursorIBeam : ui::kCursorNull; -#elif defined(OS_WIN) - static HCURSOR ibeam = LoadCursor(NULL, IDC_IBEAM); - static HCURSOR arrow = LoadCursor(NULL, IDC_ARROW); - return text_cursor ? ibeam : arrow; -#endif -} - -///////////////////////////////////////////////////////////////// -// NativeTextfieldViews, ContextMenuController overrides: -void NativeTextfieldViews::ShowContextMenuForView( - View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) { - UpdateContextMenu(); - if (context_menu_runner_->RunMenuAt(GetWidget(), NULL, - gfx::Rect(point, gfx::Size()), views::MenuItemView::TOPLEFT, - source_type, - MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU) == - MenuRunner::MENU_DELETED) - return; -} - -///////////////////////////////////////////////////////////////// -// NativeTextfieldViews, views::DragController overrides: -void NativeTextfieldViews::WriteDragDataForView(views::View* sender, - const gfx::Point& press_pt, - OSExchangeData* data) { - DCHECK_NE(ui::DragDropTypes::DRAG_NONE, - GetDragOperationsForView(sender, press_pt)); - data->SetString(GetSelectedText()); - scoped_ptr<gfx::Canvas> canvas( - views::GetCanvasForDragImage(textfield_->GetWidget(), size())); - GetRenderText()->DrawSelectedTextForDrag(canvas.get()); - drag_utils::SetDragImageOnDataObject(*canvas, size(), - press_pt.OffsetFromOrigin(), - data); - TextfieldController* controller = textfield_->GetController(); - if (controller) - controller->OnWriteDragData(data); -} - -int NativeTextfieldViews::GetDragOperationsForView(views::View* sender, - const gfx::Point& p) { - int drag_operations = ui::DragDropTypes::DRAG_COPY; - if (!textfield_->enabled() || textfield_->IsObscured() || - !GetRenderText()->IsPointInSelection(p)) - drag_operations = ui::DragDropTypes::DRAG_NONE; - else if (sender == this && !textfield_->read_only()) - drag_operations = - ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY; - TextfieldController* controller = textfield_->GetController(); - if (controller) - controller->OnGetDragOperationsForTextfield(&drag_operations); - return drag_operations; -} - -bool NativeTextfieldViews::CanStartDragForView(View* sender, - const gfx::Point& press_pt, - const gfx::Point& p) { - return initiating_drag_ && GetRenderText()->IsPointInSelection(press_pt); -} - -///////////////////////////////////////////////////////////////// -// NativeTextfieldViews, NativeTextifieldWrapper overrides: - -string16 NativeTextfieldViews::GetText() const { - return model_->GetText(); -} - -void NativeTextfieldViews::UpdateText() { - model_->SetText(GetTextForDisplay(textfield_->text())); - OnCaretBoundsChanged(); - SchedulePaint(); - textfield_->NotifyAccessibilityEvent( - ui::AccessibilityTypes::EVENT_TEXT_CHANGED, true); -} - -void NativeTextfieldViews::AppendText(const string16& text) { - if (text.empty()) - return; - model_->Append(GetTextForDisplay(text)); - OnCaretBoundsChanged(); - SchedulePaint(); -} - -void NativeTextfieldViews::InsertOrReplaceText(const string16& text) { - if (text.empty()) - return; - model_->InsertText(text); - OnCaretBoundsChanged(); - SchedulePaint(); -} - -base::i18n::TextDirection NativeTextfieldViews::GetTextDirection() const { - return GetRenderText()->GetTextDirection(); -} - -string16 NativeTextfieldViews::GetSelectedText() const { - return model_->GetSelectedText(); -} - -void NativeTextfieldViews::SelectAll(bool reversed) { - model_->SelectAll(reversed); - OnCaretBoundsChanged(); - SchedulePaint(); -} - -void NativeTextfieldViews::ClearSelection() { - model_->ClearSelection(); - OnCaretBoundsChanged(); - SchedulePaint(); -} - -void NativeTextfieldViews::UpdateBorder() { - // By default, if a caller calls Textfield::RemoveBorder() and does not set - // any explicit margins, they should get zero margins. But also call - // UpdateXXXMargins() so we respect any explicitly-set margins. - // - // NOTE: If someday Textfield supports toggling |draw_border_| back on, we'll - // need to update this conditional to set the insets to their default values. - if (!textfield_->draw_border()) - text_border_->SetInsets(0, 0, 0, 0); - UpdateHorizontalMargins(); - UpdateVerticalMargins(); -} - -void NativeTextfieldViews::UpdateTextColor() { - SetColor(textfield_->GetTextColor()); -} - -void NativeTextfieldViews::UpdateBackgroundColor() { - const SkColor color = textfield_->GetBackgroundColor(); - set_background(Background::CreateSolidBackground(color)); - GetRenderText()->set_background_is_transparent(SkColorGetA(color) != 0xFF); - SchedulePaint(); -} - -void NativeTextfieldViews::UpdateReadOnly() { - OnTextInputTypeChanged(); -} - -void NativeTextfieldViews::UpdateFont() { - GetRenderText()->SetFontList(textfield_->font_list()); - OnCaretBoundsChanged(); -} - -void NativeTextfieldViews::UpdateIsObscured() { - GetRenderText()->SetObscured(textfield_->IsObscured()); - OnCaretBoundsChanged(); - SchedulePaint(); - OnTextInputTypeChanged(); -} - -void NativeTextfieldViews::UpdateEnabled() { - SetEnabled(textfield_->enabled()); - SchedulePaint(); - OnTextInputTypeChanged(); -} - -gfx::Insets NativeTextfieldViews::CalculateInsets() { - return GetInsets(); -} - -void NativeTextfieldViews::UpdateHorizontalMargins() { - int left, right; - if (!textfield_->GetHorizontalMargins(&left, &right)) - return; - gfx::Insets inset = GetInsets(); - text_border_->SetInsets(inset.top(), left, inset.bottom(), right); - OnBoundsChanged(GetBounds()); -} - -void NativeTextfieldViews::UpdateVerticalMargins() { - int top, bottom; - if (!textfield_->GetVerticalMargins(&top, &bottom)) - return; - gfx::Insets inset = GetInsets(); - text_border_->SetInsets(top, inset.left(), bottom, inset.right()); - OnBoundsChanged(GetBounds()); -} - -bool NativeTextfieldViews::SetFocus() { - return false; -} - -View* NativeTextfieldViews::GetView() { - return this; -} - -gfx::NativeView NativeTextfieldViews::GetTestingHandle() const { - NOTREACHED(); - return NULL; -} - -bool NativeTextfieldViews::IsIMEComposing() const { - return model_->HasCompositionText(); -} - -gfx::Range NativeTextfieldViews::GetSelectedRange() const { - return GetRenderText()->selection(); -} - -void NativeTextfieldViews::SelectRange(const gfx::Range& range) { - model_->SelectRange(range); - OnCaretBoundsChanged(); - SchedulePaint(); - textfield_->NotifyAccessibilityEvent( - ui::AccessibilityTypes::EVENT_SELECTION_CHANGED, true); -} - -gfx::SelectionModel NativeTextfieldViews::GetSelectionModel() const { - return GetRenderText()->selection_model(); -} - -void NativeTextfieldViews::SelectSelectionModel( - const gfx::SelectionModel& sel) { - model_->SelectSelectionModel(sel); - OnCaretBoundsChanged(); - SchedulePaint(); -} - -size_t NativeTextfieldViews::GetCursorPosition() const { - return model_->GetCursorPosition(); -} - -bool NativeTextfieldViews::GetCursorEnabled() const { - return GetRenderText()->cursor_enabled(); -} - -void NativeTextfieldViews::SetCursorEnabled(bool enabled) { - GetRenderText()->SetCursorEnabled(enabled); -} - -bool NativeTextfieldViews::HandleKeyPressed(const ui::KeyEvent& e) { - TextfieldController* controller = textfield_->GetController(); - bool handled = false; - if (controller) - handled = controller->HandleKeyEvent(textfield_, e); - touch_selection_controller_.reset(); - return handled || HandleKeyEvent(e); -} - -bool NativeTextfieldViews::HandleKeyReleased(const ui::KeyEvent& e) { - return false; // crbug.com/127520 -} - -void NativeTextfieldViews::HandleFocus() { - GetRenderText()->set_focused(true); - is_cursor_visible_ = true; - SchedulePaint(); - GetInputMethod()->OnFocus(); - OnCaretBoundsChanged(); - - const size_t caret_blink_ms = Textfield::GetCaretBlinkMs(); - if (caret_blink_ms != 0) { - base::MessageLoop::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&NativeTextfieldViews::UpdateCursor, - cursor_timer_.GetWeakPtr()), - base::TimeDelta::FromMilliseconds(caret_blink_ms)); - } -} - -void NativeTextfieldViews::HandleBlur() { - GetRenderText()->set_focused(false); - GetInputMethod()->OnBlur(); - // Stop blinking cursor. - cursor_timer_.InvalidateWeakPtrs(); - if (is_cursor_visible_) { - is_cursor_visible_ = false; - RepaintCursor(); - } - - touch_selection_controller_.reset(); -} - -ui::TextInputClient* NativeTextfieldViews::GetTextInputClient() { - return textfield_->read_only() ? NULL : this; -} - -void NativeTextfieldViews::ClearEditHistory() { - model_->ClearEditHistory(); -} - -int NativeTextfieldViews::GetFontHeight() { - return GetRenderText()->font_list().GetHeight(); -} - -int NativeTextfieldViews::GetTextfieldBaseline() const { - return GetRenderText()->GetBaseline(); -} - -int NativeTextfieldViews::GetWidthNeededForText() const { - return GetRenderText()->GetContentWidth() + GetInsets().width(); -} - -void NativeTextfieldViews::ExecuteTextCommand(int command_id) { - ExecuteCommand(command_id, 0); -} - -bool NativeTextfieldViews::HasTextBeingDragged() { - return initiating_drag_; -} - -gfx::Point NativeTextfieldViews::GetContextMenuLocation() { - return GetCaretBounds().bottom_right(); -} - -///////////////////////////////////////////////////////////////// -// NativeTextfieldViews, ui::SimpleMenuModel::Delegate overrides: - -bool NativeTextfieldViews::IsCommandIdChecked(int command_id) const { - return true; -} - -bool NativeTextfieldViews::IsCommandIdEnabled(int command_id) const { - TextfieldController* controller = textfield_->GetController(); - if (controller && controller->HandlesCommand(command_id)) - return controller->IsCommandIdEnabled(command_id); - - bool editable = !textfield_->read_only(); - string16 result; - switch (command_id) { - case IDS_APP_UNDO: - return editable && model_->CanUndo(); - case IDS_APP_CUT: - return editable && model_->HasSelection() && !textfield_->IsObscured(); - case IDS_APP_COPY: - return model_->HasSelection() && !textfield_->IsObscured(); - case IDS_APP_PASTE: - ui::Clipboard::GetForCurrentThread()->ReadText( - ui::CLIPBOARD_TYPE_COPY_PASTE, &result); - return editable && !result.empty(); - case IDS_APP_DELETE: - return editable && model_->HasSelection(); - case IDS_APP_SELECT_ALL: - return !model_->GetText().empty(); - default: - return controller->IsCommandIdEnabled(command_id); - } -} - -bool NativeTextfieldViews::GetAcceleratorForCommandId(int command_id, - ui::Accelerator* accelerator) { - return false; -} - -bool NativeTextfieldViews::IsItemForCommandIdDynamic(int command_id) const { - const TextfieldController* controller = textfield_->GetController(); - return controller && controller->IsItemForCommandIdDynamic(command_id); -} - -string16 NativeTextfieldViews::GetLabelForCommandId(int command_id) const { - const TextfieldController* controller = textfield_->GetController(); - return controller ? controller->GetLabelForCommandId(command_id) : string16(); -} - -void NativeTextfieldViews::ExecuteCommand(int command_id, int event_flags) { - touch_selection_controller_.reset(); - if (!IsCommandIdEnabled(command_id)) - return; - - TextfieldController* controller = textfield_->GetController(); - if (controller && controller->HandlesCommand(command_id)) { - controller->ExecuteCommand(command_id, 0); - } else { - bool text_changed = false; - switch (command_id) { - case IDS_APP_UNDO: - OnBeforeUserAction(); - text_changed = model_->Undo(); - UpdateAfterChange(text_changed, text_changed); - OnAfterUserAction(); - break; - case IDS_APP_CUT: - OnBeforeUserAction(); - text_changed = Cut(); - UpdateAfterChange(text_changed, text_changed); - OnAfterUserAction(); - break; - case IDS_APP_COPY: - OnBeforeUserAction(); - Copy(); - OnAfterUserAction(); - break; - case IDS_APP_PASTE: - OnBeforeUserAction(); - text_changed = Paste(); - UpdateAfterChange(text_changed, text_changed); - OnAfterUserAction(); - break; - case IDS_APP_DELETE: - OnBeforeUserAction(); - text_changed = model_->Delete(); - UpdateAfterChange(text_changed, text_changed); - OnAfterUserAction(); - break; - case IDS_APP_SELECT_ALL: - OnBeforeUserAction(); - SelectAll(false); - UpdateAfterChange(false, true); - OnAfterUserAction(); - break; - default: - controller->ExecuteCommand(command_id, 0); - break; - } - } -} - -void NativeTextfieldViews::SetColor(SkColor value) { - GetRenderText()->SetColor(value); - SchedulePaint(); -} - -void NativeTextfieldViews::ApplyColor(SkColor value, const gfx::Range& range) { - GetRenderText()->ApplyColor(value, range); - SchedulePaint(); -} - -void NativeTextfieldViews::SetStyle(gfx::TextStyle style, bool value) { - GetRenderText()->SetStyle(style, value); - SchedulePaint(); -} - -void NativeTextfieldViews::ApplyStyle(gfx::TextStyle style, - bool value, - const gfx::Range& range) { - GetRenderText()->ApplyStyle(style, value, range); - SchedulePaint(); -} - -void NativeTextfieldViews::OnBoundsChanged(const gfx::Rect& previous_bounds) { - // Set the RenderText display area. - gfx::Insets insets = GetInsets(); - gfx::Rect display_rect(insets.left(), - insets.top(), - width() - insets.width(), - height() - insets.height()); - GetRenderText()->SetDisplayRect(display_rect); - OnCaretBoundsChanged(); -} - -/////////////////////////////////////////////////////////////////////////////// -// NativeTextfieldViews, ui::TextInputClient implementation, private: - -void NativeTextfieldViews::SetCompositionText( - const ui::CompositionText& composition) { - if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) - return; - - OnBeforeUserAction(); - skip_input_method_cancel_composition_ = true; - model_->SetCompositionText(composition); - skip_input_method_cancel_composition_ = false; - UpdateAfterChange(true, true); - OnAfterUserAction(); -} - -void NativeTextfieldViews::ConfirmCompositionText() { - if (!model_->HasCompositionText()) - return; - - OnBeforeUserAction(); - skip_input_method_cancel_composition_ = true; - model_->ConfirmCompositionText(); - skip_input_method_cancel_composition_ = false; - UpdateAfterChange(true, true); - OnAfterUserAction(); -} - -void NativeTextfieldViews::ClearCompositionText() { - if (!model_->HasCompositionText()) - return; - - OnBeforeUserAction(); - skip_input_method_cancel_composition_ = true; - model_->CancelCompositionText(); - skip_input_method_cancel_composition_ = false; - UpdateAfterChange(true, true); - OnAfterUserAction(); -} - -void NativeTextfieldViews::InsertText(const string16& text) { - // TODO(suzhe): Filter invalid characters. - if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || text.empty()) - return; - - OnBeforeUserAction(); - skip_input_method_cancel_composition_ = true; - if (GetRenderText()->insert_mode()) - model_->InsertText(GetTextForDisplay(text)); - else - model_->ReplaceText(GetTextForDisplay(text)); - skip_input_method_cancel_composition_ = false; - UpdateAfterChange(true, true); - OnAfterUserAction(); -} - -void NativeTextfieldViews::InsertChar(char16 ch, int flags) { - if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || - !ShouldInsertChar(ch, flags)) { - return; - } - - OnBeforeUserAction(); - skip_input_method_cancel_composition_ = true; - if (GetRenderText()->insert_mode()) - model_->InsertChar(ch); - else - model_->ReplaceChar(ch); - skip_input_method_cancel_composition_ = false; - - model_->SetText(GetTextForDisplay(GetText())); - - UpdateAfterChange(true, true); - OnAfterUserAction(); - - if (textfield_->IsObscured()) { - const base::TimeDelta& reveal_duration = - textfield_->obscured_reveal_duration(); - if (reveal_duration != base::TimeDelta()) { - const size_t change_offset = model_->GetCursorPosition(); - DCHECK_GT(change_offset, 0u); - RevealObscuredChar(change_offset - 1, reveal_duration); - } - } -} - -gfx::NativeWindow NativeTextfieldViews::GetAttachedWindow() const { - // Imagine the following hierarchy. - // [NativeWidget A] - FocusManager - // [View] - // [NativeWidget B] - // [View] - // [View X] - // An important thing is that [NativeWidget A] owns Win32 input focus even - // when [View X] is logically focused by FocusManager. As a result, an Win32 - // IME may want to interact with the native view of [NativeWidget A] rather - // than that of [NativeWidget B]. This is why we need to call - // GetTopLevelWidget() here. - return GetWidget()->GetTopLevelWidget()->GetNativeView(); -} - -ui::TextInputType NativeTextfieldViews::GetTextInputType() const { - return textfield_->GetTextInputType(); -} - -ui::TextInputMode NativeTextfieldViews::GetTextInputMode() const { - return ui::TEXT_INPUT_MODE_DEFAULT; -} - -bool NativeTextfieldViews::CanComposeInline() const { - return true; -} - -gfx::Rect NativeTextfieldViews::GetCaretBounds() const { - // TextInputClient::GetCaretBounds is expected to return a value in screen - // coordinates. - gfx::Rect rect = GetRenderText()->GetUpdatedCursorBounds(); - ConvertRectToScreen(this, &rect); - return rect; -} - -bool NativeTextfieldViews::GetCompositionCharacterBounds( - uint32 index, - gfx::Rect* rect) const { - DCHECK(rect); - if (!HasCompositionText()) - return false; - const gfx::Range& composition_range = GetRenderText()->GetCompositionRange(); - DCHECK(!composition_range.is_empty()); - - size_t text_index = composition_range.start() + index; - if (composition_range.end() <= text_index) - return false; - if (!GetRenderText()->IsCursorablePosition(text_index)) { - text_index = GetRenderText()->IndexOfAdjacentGrapheme( - text_index, gfx::CURSOR_BACKWARD); - } - if (text_index < composition_range.start()) - return false; - const gfx::SelectionModel caret(text_index, gfx::CURSOR_BACKWARD); - *rect = GetRenderText()->GetCursorBounds(caret, false); - ConvertRectToScreen(this, rect); - - return true; -} - -bool NativeTextfieldViews::HasCompositionText() const { - return model_->HasCompositionText(); -} - -bool NativeTextfieldViews::GetTextRange(gfx::Range* range) const { - if (!ImeEditingAllowed()) - return false; - - model_->GetTextRange(range); - return true; -} - -bool NativeTextfieldViews::GetCompositionTextRange(gfx::Range* range) const { - if (!ImeEditingAllowed()) - return false; - - model_->GetCompositionTextRange(range); - return true; -} - -bool NativeTextfieldViews::GetSelectionRange(gfx::Range* range) const { - if (!ImeEditingAllowed()) - return false; - *range = GetSelectedRange(); - return true; -} - -bool NativeTextfieldViews::SetSelectionRange(const gfx::Range& range) { - if (!ImeEditingAllowed() || !range.IsValid()) - return false; - - OnBeforeUserAction(); - SelectRange(range); - OnAfterUserAction(); - return true; -} - -bool NativeTextfieldViews::DeleteRange(const gfx::Range& range) { - if (!ImeEditingAllowed() || range.is_empty()) - return false; - - OnBeforeUserAction(); - model_->SelectRange(range); - if (model_->HasSelection()) { - model_->DeleteSelection(); - UpdateAfterChange(true, true); - } - OnAfterUserAction(); - return true; -} - -bool NativeTextfieldViews::GetTextFromRange( - const gfx::Range& range, - string16* text) const { - if (!ImeEditingAllowed() || !range.IsValid()) - return false; - - gfx::Range text_range; - if (!GetTextRange(&text_range) || !text_range.Contains(range)) - return false; - - *text = model_->GetTextFromRange(range); - return true; -} - -void NativeTextfieldViews::OnInputMethodChanged() { - // TODO(msw): NOTIMPLEMENTED(); see http://crbug.com/140402 -} - -bool NativeTextfieldViews::ChangeTextDirectionAndLayoutAlignment( - base::i18n::TextDirection direction) { - // Restore text directionality mode when the indicated direction matches the - // current forced mode; otherwise, force the mode indicated. This helps users - // manage BiDi text layout without getting stuck in forced LTR or RTL modes. - const gfx::DirectionalityMode mode = direction == base::i18n::RIGHT_TO_LEFT ? - gfx::DIRECTIONALITY_FORCE_RTL : gfx::DIRECTIONALITY_FORCE_LTR; - if (mode == GetRenderText()->directionality_mode()) - GetRenderText()->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_TEXT); - else - GetRenderText()->SetDirectionalityMode(mode); - SchedulePaint(); - return true; -} - -void NativeTextfieldViews::ExtendSelectionAndDelete( - size_t before, - size_t after) { - gfx::Range range = GetSelectedRange(); - DCHECK_GE(range.start(), before); - - range.set_start(range.start() - before); - range.set_end(range.end() + after); - gfx::Range text_range; - if (GetTextRange(&text_range) && text_range.Contains(range)) - DeleteRange(range); -} - -void NativeTextfieldViews::EnsureCaretInRect(const gfx::Rect& rect) { -} - -void NativeTextfieldViews::OnCandidateWindowShown() { -} - -void NativeTextfieldViews::OnCandidateWindowUpdated() { -} - -void NativeTextfieldViews::OnCandidateWindowHidden() { -} - -void NativeTextfieldViews::OnCompositionTextConfirmedOrCleared() { - if (skip_input_method_cancel_composition_) - return; - DCHECK(textfield_->GetInputMethod()); - textfield_->GetInputMethod()->CancelComposition(textfield_); -} - -gfx::RenderText* NativeTextfieldViews::GetRenderText() const { - return model_->render_text(); -} - -string16 NativeTextfieldViews::GetTextForDisplay(const string16& text) { - return textfield_->style() & Textfield::STYLE_LOWERCASE ? - base::i18n::ToLower(text) : text; -} - -void NativeTextfieldViews::UpdateColorsFromTheme(const ui::NativeTheme* theme) { - UpdateTextColor(); - UpdateBackgroundColor(); - gfx::RenderText* render_text = GetRenderText(); - render_text->set_cursor_color(textfield_->GetTextColor()); - render_text->set_selection_color(theme->GetSystemColor( - ui::NativeTheme::kColorId_TextfieldSelectionColor)); - render_text->set_selection_background_focused_color(theme->GetSystemColor( - ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused)); -} - -void NativeTextfieldViews::UpdateCursor() { - const size_t caret_blink_ms = Textfield::GetCaretBlinkMs(); - is_cursor_visible_ = !is_cursor_visible_ || (caret_blink_ms == 0); - RepaintCursor(); - if (caret_blink_ms != 0) { - base::MessageLoop::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&NativeTextfieldViews::UpdateCursor, - cursor_timer_.GetWeakPtr()), - base::TimeDelta::FromMilliseconds(caret_blink_ms)); - } -} - -void NativeTextfieldViews::RepaintCursor() { - gfx::Rect r(GetRenderText()->GetUpdatedCursorBounds()); - r.Inset(-1, -1, -1, -1); - SchedulePaintInRect(r); -} - -void NativeTextfieldViews::PaintTextAndCursor(gfx::Canvas* canvas) { - TRACE_EVENT0("views", "NativeTextfieldViews::PaintTextAndCursor"); - canvas->Save(); - GetRenderText()->set_cursor_visible(!is_drop_cursor_visible_ && - is_cursor_visible_ && !model_->HasSelection()); - // Draw the text, cursor, and selection. - GetRenderText()->Draw(canvas); - - // Draw the detached drop cursor that marks where the text will be dropped. - if (is_drop_cursor_visible_) - GetRenderText()->DrawCursor(canvas, drop_cursor_position_); - - // Draw placeholder text if needed. - if (model_->GetText().empty() && - !textfield_->GetPlaceholderText().empty()) { - canvas->DrawStringInt( - textfield_->GetPlaceholderText(), - GetRenderText()->GetPrimaryFont(), - textfield_->placeholder_text_color(), - GetRenderText()->display_rect()); - } - canvas->Restore(); -} - -bool NativeTextfieldViews::HandleKeyEvent(const ui::KeyEvent& key_event) { - // TODO(oshima): Refactor and consolidate with ExecuteCommand. - if (key_event.type() == ui::ET_KEY_PRESSED) { - ui::KeyboardCode key_code = key_event.key_code(); - if (key_code == ui::VKEY_TAB || key_event.IsUnicodeKeyCode()) - return false; - - OnBeforeUserAction(); - const bool editable = !textfield_->read_only(); - const bool readable = !textfield_->IsObscured(); - const bool shift = key_event.IsShiftDown(); - const bool control = key_event.IsControlDown(); - const bool alt = key_event.IsAltDown() || key_event.IsAltGrDown(); - bool text_changed = false; - bool cursor_changed = false; - switch (key_code) { - case ui::VKEY_Z: - if (control && !shift && !alt && editable) - cursor_changed = text_changed = model_->Undo(); - else if (control && shift && !alt && editable) - cursor_changed = text_changed = model_->Redo(); - break; - case ui::VKEY_Y: - if (control && !alt && editable) - cursor_changed = text_changed = model_->Redo(); - break; - case ui::VKEY_A: - if (control && !alt) { - model_->SelectAll(false); - cursor_changed = true; - } - break; - case ui::VKEY_X: - if (control && !alt && editable && readable) - cursor_changed = text_changed = Cut(); - break; - case ui::VKEY_C: - if (control && !alt && readable) - Copy(); - break; - case ui::VKEY_V: - if (control && !alt && editable) - cursor_changed = text_changed = Paste(); - break; - case ui::VKEY_RIGHT: - case ui::VKEY_LEFT: { - // We should ignore the alt-left/right keys because alt key doesn't make - // any special effects for them and they can be shortcut keys such like - // forward/back of the browser history. - if (alt) - break; - const gfx::Range selection_range = GetSelectedRange(); - model_->MoveCursor( - control ? gfx::WORD_BREAK : gfx::CHARACTER_BREAK, - (key_code == ui::VKEY_RIGHT) ? gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT, - shift); - cursor_changed = GetSelectedRange() != selection_range; - break; - } - case ui::VKEY_END: - case ui::VKEY_HOME: - if ((key_code == ui::VKEY_HOME) == - (GetRenderText()->GetTextDirection() == base::i18n::RIGHT_TO_LEFT)) - model_->MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, shift); - else - model_->MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, shift); - cursor_changed = true; - break; - case ui::VKEY_BACK: - case ui::VKEY_DELETE: - if (!editable) - break; - if (!model_->HasSelection()) { - gfx::VisualCursorDirection direction = (key_code == ui::VKEY_DELETE) ? - gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT; - if (shift && control) { - // If both shift and control are pressed, then erase up to the - // beginning/end of the buffer in ChromeOS. In windows, do nothing. -#if defined(OS_WIN) - break; -#else - model_->MoveCursor(gfx::LINE_BREAK, direction, true); -#endif - } else if (control) { - // If only control is pressed, then erase the previous/next word. - model_->MoveCursor(gfx::WORD_BREAK, direction, true); - } - } - if (key_code == ui::VKEY_BACK) - model_->Backspace(); - else if (shift && model_->HasSelection() && readable) - Cut(); - else - model_->Delete(); - - // Consume backspace and delete keys even if the edit did nothing. This - // prevents potential unintended side-effects of further event handling. - text_changed = true; - break; - case ui::VKEY_INSERT: - if (control && !shift && readable) - Copy(); - else if (shift && !control && editable) - cursor_changed = text_changed = Paste(); - break; - default: - break; - } - - // We must have input method in order to support text input. - DCHECK(textfield_->GetInputMethod()); - - UpdateAfterChange(text_changed, cursor_changed); - OnAfterUserAction(); - return (text_changed || cursor_changed); - } - return false; -} - -bool NativeTextfieldViews::MoveCursorTo(const gfx::Point& point, bool select) { - if (!model_->MoveCursorTo(point, select)) - return false; - OnCaretBoundsChanged(); - return true; -} - -void NativeTextfieldViews::PropagateTextChange() { - textfield_->SyncText(); -} - -void NativeTextfieldViews::UpdateAfterChange(bool text_changed, - bool cursor_changed) { - if (text_changed) { - PropagateTextChange(); - textfield_->NotifyAccessibilityEvent( - ui::AccessibilityTypes::EVENT_TEXT_CHANGED, true); - } - if (cursor_changed) { - is_cursor_visible_ = true; - RepaintCursor(); - if (!text_changed) { - // TEXT_CHANGED implies SELECTION_CHANGED, so we only need to fire - // this if only the selection changed. - textfield_->NotifyAccessibilityEvent( - ui::AccessibilityTypes::EVENT_SELECTION_CHANGED, true); - } - } - if (text_changed || cursor_changed) { - OnCaretBoundsChanged(); - SchedulePaint(); - } -} - -void NativeTextfieldViews::UpdateContextMenu() { - if (!context_menu_contents_.get()) { - context_menu_contents_.reset(new ui::SimpleMenuModel(this)); - context_menu_contents_->AddItemWithStringId(IDS_APP_UNDO, IDS_APP_UNDO); - context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR); - context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT); - context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY); - context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE); - context_menu_contents_->AddItemWithStringId(IDS_APP_DELETE, IDS_APP_DELETE); - context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR); - context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL, - IDS_APP_SELECT_ALL); - TextfieldController* controller = textfield_->GetController(); - if (controller) - controller->UpdateContextMenu(context_menu_contents_.get()); - - context_menu_delegate_.reset( - new views::MenuModelAdapter(context_menu_contents_.get())); - context_menu_runner_.reset( - new MenuRunner(new views::MenuItemView(context_menu_delegate_.get()))); - } - - context_menu_delegate_->BuildMenu(context_menu_runner_->GetMenu()); -} - -void NativeTextfieldViews::OnTextInputTypeChanged() { - // TODO(suzhe): changed from DCHECK. See http://crbug.com/81320. - if (textfield_->GetInputMethod()) - textfield_->GetInputMethod()->OnTextInputTypeChanged(textfield_); -} - -void NativeTextfieldViews::OnCaretBoundsChanged() { - // TODO(suzhe): changed from DCHECK. See http://crbug.com/81320. - if (textfield_->GetInputMethod()) - textfield_->GetInputMethod()->OnCaretBoundsChanged(textfield_); - - // Notify selection controller - if (touch_selection_controller_.get()) - touch_selection_controller_->SelectionChanged(); -} - -void NativeTextfieldViews::OnBeforeUserAction() { - TextfieldController* controller = textfield_->GetController(); - if (controller) - controller->OnBeforeUserAction(textfield_); -} - -void NativeTextfieldViews::OnAfterUserAction() { - TextfieldController* controller = textfield_->GetController(); - if (controller) - controller->OnAfterUserAction(textfield_); -} - -bool NativeTextfieldViews::Cut() { - if (!textfield_->read_only() && !textfield_->IsObscured() && model_->Cut()) { - TextfieldController* controller = textfield_->GetController(); - if (controller) - controller->OnAfterCutOrCopy(); - return true; - } - return false; -} - -bool NativeTextfieldViews::Copy() { - if (!textfield_->IsObscured() && model_->Copy()) { - TextfieldController* controller = textfield_->GetController(); - if (controller) - controller->OnAfterCutOrCopy(); - return true; - } - return false; -} - -bool NativeTextfieldViews::Paste() { - if (textfield_->read_only()) - return false; - - const string16 original_text = GetText(); - const bool success = model_->Paste(); - - if (success) { - // As Paste is handled in model_->Paste(), the RenderText may contain - // upper case characters. This is not consistent with other places - // which keeps RenderText only containing lower case characters. - string16 new_text = GetTextForDisplay(GetText()); - model_->SetText(new_text); - - TextfieldController* controller = textfield_->GetController(); - if (controller) - controller->OnAfterPaste(); - } - return success; -} - -void NativeTextfieldViews::TrackMouseClicks(const ui::MouseEvent& event) { - if (event.IsOnlyLeftMouseButton()) { - base::TimeDelta time_delta = event.time_stamp() - last_click_time_; - if (time_delta.InMilliseconds() <= GetDoubleClickInterval() && - !ExceededDragThresholdFromLastClickLocation(event)) { - // Upon clicking after a triple click, the count should go back to double - // click and alternate between double and triple. This assignment maps - // 0 to 1, 1 to 2, 2 to 1. - aggregated_clicks_ = (aggregated_clicks_ % 2) + 1; - } else { - aggregated_clicks_ = 0; - } - last_click_time_ = event.time_stamp(); - last_click_location_ = event.location(); - } -} - -void NativeTextfieldViews::HandleMousePressEvent(const ui::MouseEvent& event) { - if (event.IsOnlyLeftMouseButton() || event.IsOnlyRightMouseButton()) - textfield_->RequestFocus(); - - if (!event.IsOnlyLeftMouseButton()) - return; - - initiating_drag_ = false; - bool can_drag = true; - - switch (aggregated_clicks_) { - case 0: - if (can_drag && GetRenderText()->IsPointInSelection(event.location())) - initiating_drag_ = true; - else - MoveCursorTo(event.location(), event.IsShiftDown()); - break; - case 1: - MoveCursorTo(event.location(), false); - model_->SelectWord(); - double_click_word_ = GetRenderText()->selection(); - OnCaretBoundsChanged(); - break; - case 2: - model_->SelectAll(false); - OnCaretBoundsChanged(); - break; - default: - NOTREACHED(); - } - SchedulePaint(); -} - -bool NativeTextfieldViews::ImeEditingAllowed() const { - // We don't allow the input method to retrieve or delete content from a - // password field. - ui::TextInputType t = GetTextInputType(); - return (t != ui::TEXT_INPUT_TYPE_NONE && t != ui::TEXT_INPUT_TYPE_PASSWORD); -} - -// static -bool NativeTextfieldViews::ShouldInsertChar(char16 ch, int flags) { - // Filter out all control characters, including tab and new line characters, - // and all characters with Alt modifier. But we need to allow characters with - // AltGr modifier. - // On Windows AltGr is represented by Alt+Ctrl, and on Linux it's a different - // flag that we don't care about. - return ((ch >= 0x20 && ch < 0x7F) || ch > 0x9F) && - (flags & ~(ui::EF_SHIFT_DOWN | ui::EF_CAPS_LOCK_DOWN)) != ui::EF_ALT_DOWN; -} - -void NativeTextfieldViews::CreateTouchSelectionControllerAndNotifyIt() { - if (!touch_selection_controller_) { - touch_selection_controller_.reset( - ui::TouchSelectionController::create(this)); - } - if (touch_selection_controller_) - touch_selection_controller_->SelectionChanged(); -} - -void NativeTextfieldViews::PlatformGestureEventHandling( - const ui::GestureEvent* event) { -#if defined(OS_WIN) && defined(USE_AURA) - if (event->type() == ui::ET_GESTURE_TAP && !textfield_->read_only()) - base::win::DisplayVirtualKeyboard(); -#endif -} - -void NativeTextfieldViews::RevealObscuredChar(int index, - const base::TimeDelta& duration) { - GetRenderText()->SetObscuredRevealIndex(index); - SchedulePaint(); - - if (index != -1) { - obscured_reveal_timer_.Start( - FROM_HERE, - duration, - base::Bind(&NativeTextfieldViews::RevealObscuredChar, - base::Unretained(this), -1, base::TimeDelta())); - } -} - -} // namespace views diff --git a/chromium/ui/views/controls/textfield/native_textfield_views.h b/chromium/ui/views/controls/textfield/native_textfield_views.h deleted file mode 100644 index e85f44dd1b6..00000000000 --- a/chromium/ui/views/controls/textfield/native_textfield_views.h +++ /dev/null @@ -1,345 +0,0 @@ -// 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_CONTROLS_TEXTFIELD_NATIVE_TEXTFIELD_VIEWS_H_ -#define UI_VIEWS_CONTROLS_TEXTFIELD_NATIVE_TEXTFIELD_VIEWS_H_ - -#include "base/memory/weak_ptr.h" -#include "base/strings/string16.h" -#include "base/timer/timer.h" -#include "ui/base/ime/text_input_client.h" -#include "ui/base/models/simple_menu_model.h" -#include "ui/base/touch/touch_editing_controller.h" -#include "ui/events/event_constants.h" -#include "ui/gfx/font.h" -#include "ui/views/border.h" -#include "ui/views/context_menu_controller.h" -#include "ui/views/controls/textfield/native_textfield_wrapper.h" -#include "ui/views/controls/textfield/textfield_views_model.h" -#include "ui/views/drag_controller.h" -#include "ui/views/view.h" - -namespace base { -class Time; -} - -namespace gfx { -class Canvas; -} - -namespace views { - -class FocusableBorder; -class MenuModelAdapter; -class MenuRunner; - -// A views/skia only implementation of NativeTextfieldWrapper. -// No platform specific code is used. -// Following features are not yet supported. -// * BIDI/Complex script. -// * Support surrogate pair, or maybe we should just use UTF32 internally. -// * X selection (only if we want to support). -// Once completed, this will replace Textfield, NativeTextfieldWin and -// NativeTextfieldGtk. -class VIEWS_EXPORT NativeTextfieldViews : public View, - public ui::TouchEditable, - public ContextMenuController, - public DragController, - public NativeTextfieldWrapper, - public ui::TextInputClient, - public TextfieldViewsModel::Delegate { - public: - explicit NativeTextfieldViews(Textfield* parent); - virtual ~NativeTextfieldViews(); - - // View overrides: - virtual gfx::NativeCursor GetCursor(const ui::MouseEvent& event) 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 OnGestureEvent(ui::GestureEvent* event) OVERRIDE; - virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE; - virtual bool GetDropFormats( - int* formats, - std::set<ui::OSExchangeData::CustomFormat>* custom_formats) OVERRIDE; - virtual bool CanDrop(const ui::OSExchangeData& data) OVERRIDE; - virtual int OnDragUpdated(const ui::DropTargetEvent& event) OVERRIDE; - virtual void OnDragExited() OVERRIDE; - virtual int OnPerformDrop(const ui::DropTargetEvent& event) OVERRIDE; - virtual void OnDragDone() OVERRIDE; - virtual bool OnKeyReleased(const ui::KeyEvent& event) OVERRIDE; - virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; - virtual void OnFocus() OVERRIDE; - virtual void OnBlur() OVERRIDE; - virtual void OnNativeThemeChanged(const ui::NativeTheme* theme) OVERRIDE; - - // ui::TouchEditable overrides: - virtual void SelectRect(const gfx::Point& start, - const gfx::Point& end) OVERRIDE; - virtual void MoveCaretTo(const gfx::Point& point) OVERRIDE; - virtual void GetSelectionEndPoints(gfx::Rect* p1, gfx::Rect* p2) OVERRIDE; - virtual gfx::Rect GetBounds() OVERRIDE; - virtual gfx::NativeView GetNativeView() OVERRIDE; - virtual void ConvertPointToScreen(gfx::Point* point) OVERRIDE; - virtual void ConvertPointFromScreen(gfx::Point* point) OVERRIDE; - virtual bool DrawsHandles() OVERRIDE; - virtual void OpenContextMenu(const gfx::Point& anchor) OVERRIDE; - - // ContextMenuController overrides: - virtual void ShowContextMenuForView(View* source, - const gfx::Point& point, - ui::MenuSourceType source_type) OVERRIDE; - - // Overridden from DragController: - virtual void WriteDragDataForView(View* sender, - const gfx::Point& press_pt, - ui::OSExchangeData* data) OVERRIDE; - virtual int GetDragOperationsForView(View* sender, - const gfx::Point& p) OVERRIDE; - virtual bool CanStartDragForView(View* sender, - const gfx::Point& press_pt, - const gfx::Point& p) OVERRIDE; - - // NativeTextfieldWrapper overrides: - virtual string16 GetText() const OVERRIDE; - virtual void UpdateText() OVERRIDE; - virtual void AppendText(const string16& text) OVERRIDE; - virtual void InsertOrReplaceText(const string16& text) OVERRIDE; - virtual base::i18n::TextDirection GetTextDirection() const OVERRIDE; - virtual string16 GetSelectedText() const OVERRIDE; - virtual void SelectAll(bool reversed) OVERRIDE; - virtual void ClearSelection() OVERRIDE; - virtual void UpdateBorder() OVERRIDE; - virtual void UpdateTextColor() OVERRIDE; - virtual void UpdateBackgroundColor() OVERRIDE; - virtual void UpdateReadOnly() OVERRIDE; - virtual void UpdateFont() OVERRIDE; - virtual void UpdateIsObscured() OVERRIDE; - virtual void UpdateEnabled() OVERRIDE; - virtual gfx::Insets CalculateInsets() OVERRIDE; - virtual void UpdateHorizontalMargins() OVERRIDE; - virtual void UpdateVerticalMargins() OVERRIDE; - virtual bool SetFocus() OVERRIDE; - virtual View* GetView() OVERRIDE; - virtual gfx::NativeView GetTestingHandle() const OVERRIDE; - virtual bool IsIMEComposing() const OVERRIDE; - virtual gfx::Range GetSelectedRange() const OVERRIDE; - virtual void SelectRange(const gfx::Range& range) OVERRIDE; - virtual gfx::SelectionModel GetSelectionModel() const OVERRIDE; - virtual void SelectSelectionModel(const gfx::SelectionModel& sel) OVERRIDE; - virtual size_t GetCursorPosition() const OVERRIDE; - virtual bool GetCursorEnabled() const OVERRIDE; - virtual void SetCursorEnabled(bool enabled) OVERRIDE; - virtual bool HandleKeyPressed(const ui::KeyEvent& e) OVERRIDE; - virtual bool HandleKeyReleased(const ui::KeyEvent& e) OVERRIDE; - virtual void HandleFocus() OVERRIDE; - virtual void HandleBlur() OVERRIDE; - virtual ui::TextInputClient* GetTextInputClient() OVERRIDE; - virtual void SetColor(SkColor value) OVERRIDE; - virtual void ApplyColor(SkColor value, const gfx::Range& range) OVERRIDE; - virtual void SetStyle(gfx::TextStyle style, bool value) OVERRIDE; - virtual void ApplyStyle(gfx::TextStyle style, - bool value, - const gfx::Range& range) OVERRIDE; - virtual void ClearEditHistory() OVERRIDE; - virtual int GetFontHeight() OVERRIDE; - virtual int GetTextfieldBaseline() const OVERRIDE; - virtual int GetWidthNeededForText() const OVERRIDE; - virtual void ExecuteTextCommand(int command_id) OVERRIDE; - virtual bool HasTextBeingDragged() OVERRIDE; - virtual gfx::Point GetContextMenuLocation() OVERRIDE; - - // ui::SimpleMenuModel::Delegate overrides - virtual bool IsCommandIdChecked(int command_id) const OVERRIDE; - virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE; - virtual bool GetAcceleratorForCommandId( - int command_id, - ui::Accelerator* accelerator) OVERRIDE; - virtual bool IsItemForCommandIdDynamic(int command_id) const OVERRIDE; - virtual string16 GetLabelForCommandId(int command_id) const OVERRIDE; - virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE; - - // class name of internal - static const char kViewClassName[]; - - protected: - // View override. - virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE; - - private: - friend class NativeTextfieldViewsTest; - friend class TouchSelectionControllerImplTest; - - // Overridden from ui::TextInputClient: - virtual void SetCompositionText( - const ui::CompositionText& composition) OVERRIDE; - virtual void ConfirmCompositionText() OVERRIDE; - virtual void ClearCompositionText() OVERRIDE; - virtual void InsertText(const string16& text) OVERRIDE; - virtual void InsertChar(char16 ch, int flags) OVERRIDE; - virtual gfx::NativeWindow GetAttachedWindow() const OVERRIDE; - virtual ui::TextInputType GetTextInputType() const OVERRIDE; - virtual ui::TextInputMode GetTextInputMode() const OVERRIDE; - virtual bool CanComposeInline() const OVERRIDE; - virtual gfx::Rect GetCaretBounds() const OVERRIDE; - virtual bool GetCompositionCharacterBounds(uint32 index, - gfx::Rect* rect) const OVERRIDE; - virtual bool HasCompositionText() const OVERRIDE; - virtual bool GetTextRange(gfx::Range* range) const OVERRIDE; - virtual bool GetCompositionTextRange(gfx::Range* range) const OVERRIDE; - virtual bool GetSelectionRange(gfx::Range* range) const OVERRIDE; - virtual bool SetSelectionRange(const gfx::Range& range) OVERRIDE; - virtual bool DeleteRange(const gfx::Range& range) OVERRIDE; - virtual bool GetTextFromRange(const gfx::Range& range, - string16* text) const OVERRIDE; - virtual void OnInputMethodChanged() OVERRIDE; - virtual bool ChangeTextDirectionAndLayoutAlignment( - base::i18n::TextDirection direction) OVERRIDE; - virtual void ExtendSelectionAndDelete(size_t before, size_t after) OVERRIDE; - virtual void EnsureCaretInRect(const gfx::Rect& rect) OVERRIDE; - virtual void OnCandidateWindowShown() OVERRIDE; - virtual void OnCandidateWindowUpdated() OVERRIDE; - virtual void OnCandidateWindowHidden() OVERRIDE; - - // Overridden from TextfieldViewsModel::Delegate: - virtual void OnCompositionTextConfirmedOrCleared() OVERRIDE; - - // Returns the TextfieldViewsModel's text/cursor/selection rendering model. - gfx::RenderText* GetRenderText() const; - - // Converts |text| according to textfield style, e.g. lower case if - // |textfield_| has STYLE_LOWERCASE style. - string16 GetTextForDisplay(const string16& text); - - // Updates any colors that have not been explicitly set from the theme. - void UpdateColorsFromTheme(const ui::NativeTheme* theme); - - // A callback function to periodically update the cursor state. - void UpdateCursor(); - - // Repaint the cursor. - void RepaintCursor(); - - // Update the cursor_bounds and text_offset. - void UpdateCursorBoundsAndTextOffset(size_t cursor_pos, bool insert_mode); - - void PaintTextAndCursor(gfx::Canvas* canvas); - - // Handle the keyevent. - bool HandleKeyEvent(const ui::KeyEvent& key_event); - - // Helper function to call MoveCursorTo on the TextfieldViewsModel. - bool MoveCursorTo(const gfx::Point& point, bool select); - - // Utility function to inform the parent textfield (and its controller if any) - // that the text in the textfield has changed. - void PropagateTextChange(); - - // Does necessary updates when the text and/or the position of the cursor - // changed. - void UpdateAfterChange(bool text_changed, bool cursor_changed); - - // Utility function to prepare the context menu. - void UpdateContextMenu(); - - // Convenience method to call InputMethod::OnTextInputTypeChanged(); - void OnTextInputTypeChanged(); - - // Convenience method to call InputMethod::OnCaretBoundsChanged(); - void OnCaretBoundsChanged(); - - // Convenience method to call TextfieldController::OnBeforeUserAction(); - void OnBeforeUserAction(); - - // Convenience method to call TextfieldController::OnAfterUserAction(); - void OnAfterUserAction(); - - // Calls |model_->Cut()| and notifies TextfieldController on success. - bool Cut(); - - // Calls |model_->Copy()| and notifies TextfieldController on success. - bool Copy(); - - // Calls |model_->Paste()| and calls TextfieldController::ContentsChanged() - // explicitly if paste succeeded. - bool Paste(); - - // Tracks the mouse clicks for single/double/triple clicks. - void TrackMouseClicks(const ui::MouseEvent& event); - - // Handles mouse press events. - void HandleMousePressEvent(const ui::MouseEvent& event); - - // Returns true if the current text input type allows access by the IME. - bool ImeEditingAllowed() const; - - // Returns true if distance between |event| and |last_click_location_| - // exceeds the drag threshold. - bool ExceededDragThresholdFromLastClickLocation(const ui::MouseEvent& event); - - // Checks if a char is ok to be inserted into the textfield. The |ch| is a - // modified character, i.e., modifiers took effect when generating this char. - static bool ShouldInsertChar(char16 ch, int flags); - - void CreateTouchSelectionControllerAndNotifyIt(); - - // Platform specific gesture event handling. - void PlatformGestureEventHandling(const ui::GestureEvent* event); - - // Reveals the obscured char at |index| for the given |duration|. If |index| - // is -1, existing revealed index will be cleared. - void RevealObscuredChar(int index, const base::TimeDelta& duration); - - // The parent textfield, the owner of this object. - Textfield* textfield_; - - // The text model. - scoped_ptr<TextfieldViewsModel> model_; - - // The focusable border. This is always non-NULL, but may not actually be - // drawn. If it is not drawn, then by default it's also zero-sized unless the - // Textfield has explicitly-set margins. - FocusableBorder* text_border_; - - // The textfield's text and drop cursor visibility. - bool is_cursor_visible_; - - // The drop cursor is a visual cue for where dragged text will be dropped. - bool is_drop_cursor_visible_; - // Position of the drop cursor, if it is visible. - gfx::SelectionModel drop_cursor_position_; - - // True if InputMethod::CancelComposition() should not be called. - bool skip_input_method_cancel_composition_; - - // Is the user potentially dragging and dropping from this view? - bool initiating_drag_; - - // A runnable method factory for callback to update the cursor. - base::WeakPtrFactory<NativeTextfieldViews> cursor_timer_; - - // State variables used to track double and triple clicks. - size_t aggregated_clicks_; - base::TimeDelta last_click_time_; - gfx::Point last_click_location_; - gfx::Range double_click_word_; - - // Context menu and its content list for the textfield. - scoped_ptr<ui::SimpleMenuModel> context_menu_contents_; - scoped_ptr<views::MenuModelAdapter> context_menu_delegate_; - scoped_ptr<views::MenuRunner> context_menu_runner_; - - scoped_ptr<ui::TouchSelectionController> touch_selection_controller_; - - // A timer to control the duration of showing the last typed char in - // obscured text. When the timer is running, the last typed char is shown - // and when the time expires, the last typed char is obscured. - base::OneShotTimer<NativeTextfieldViews> obscured_reveal_timer_; - - DISALLOW_COPY_AND_ASSIGN(NativeTextfieldViews); -}; - -} // namespace views - -#endif // UI_VIEWS_CONTROLS_TEXTFIELD_NATIVE_TEXTFIELD_VIEWS_H_ diff --git a/chromium/ui/views/controls/textfield/native_textfield_wrapper.h b/chromium/ui/views/controls/textfield/native_textfield_wrapper.h deleted file mode 100644 index eafd2f59dd7..00000000000 --- a/chromium/ui/views/controls/textfield/native_textfield_wrapper.h +++ /dev/null @@ -1,188 +0,0 @@ -// 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_CONTROLS_TEXTFIELD_NATIVE_TEXTFIELD_WRAPPER_H_ -#define UI_VIEWS_CONTROLS_TEXTFIELD_NATIVE_TEXTFIELD_WRAPPER_H_ - -#include "base/i18n/rtl.h" -#include "base/strings/string16.h" -#include "ui/gfx/native_widget_types.h" -#include "ui/gfx/range/range.h" -#include "ui/gfx/selection_model.h" -#include "ui/gfx/text_constants.h" -#include "ui/views/views_export.h" - -namespace gfx { -class Insets; -class Point; -} // namespace gfx - -namespace ui { -class KeyEvent; -class TextInputClient; -} // namespace ui - -namespace views { - -class Textfield; -class View; - -// An interface implemented by an object that provides a platform-native -// text field. -class VIEWS_EXPORT NativeTextfieldWrapper { - public: - // The Textfield calls this when it is destroyed to clean up the wrapper - // object. - virtual ~NativeTextfieldWrapper() {} - - // Gets the text displayed in the wrapped native text field. - virtual string16 GetText() const = 0; - - // Updates the text displayed with the text held by the Textfield. - virtual void UpdateText() = 0; - - // Adds the specified text to the text already displayed by the wrapped native - // text field. - virtual void AppendText(const string16& text) = 0; - - // Inserts |text| at the current cursor position, replacing any selected text. - virtual void InsertOrReplaceText(const string16& text) = 0; - - // Returns the text direction. - virtual base::i18n::TextDirection GetTextDirection() const = 0; - - // Gets the text that is selected in the wrapped native text field. - virtual string16 GetSelectedText() const = 0; - - // Select the entire text range. If |reversed| is true, the range will end at - // the logical beginning of the text; this generally shows the leading portion - // of text that overflows its display area. - virtual void SelectAll(bool reversed) = 0; - - // Clears the selection within the edit field and sets the caret to the end. - virtual void ClearSelection() = 0; - - // Updates whether there is a visible border. - virtual void UpdateBorder() = 0; - - // Updates the text color used when painting the native text field. - virtual void UpdateTextColor() = 0; - - // Updates the background color used when painting the native text field. - virtual void UpdateBackgroundColor() = 0; - - // Updates the read-only state of the native text field. - virtual void UpdateReadOnly() = 0; - - // Updates the font used to render text in the native text field. - virtual void UpdateFont() = 0; - - // Updates the visibility of the text in the native text field. - virtual void UpdateIsObscured() = 0; - - // Updates the enabled state of the native text field. - virtual void UpdateEnabled() = 0; - - // Returns the insets for the text field. - virtual gfx::Insets CalculateInsets() = 0; - - // Updates the horizontal margins for the native text field. - virtual void UpdateHorizontalMargins() = 0; - - // Updates the vertical margins for the native text field. - virtual void UpdateVerticalMargins() = 0; - - // Sets the focus to the text field. Returns false if the wrapper - // didn't take focus. - virtual bool SetFocus() = 0; - - // Retrieves the views::View that hosts the native control. - virtual View* GetView() = 0; - - // Returns a handle to the underlying native view for testing. - virtual gfx::NativeView GetTestingHandle() const = 0; - - // Returns whether or not an IME is composing text. - virtual bool IsIMEComposing() const = 0; - - // Gets the selected range. - virtual gfx::Range GetSelectedRange() const = 0; - - // Selects the text given by |range|. - virtual void SelectRange(const gfx::Range& range) = 0; - - // Gets the selection model. - virtual gfx::SelectionModel GetSelectionModel() const = 0; - - // Selects the text given by |sel|. - virtual void SelectSelectionModel(const gfx::SelectionModel& sel) = 0; - - // Returns the currnet cursor position. - virtual size_t GetCursorPosition() const = 0; - - // Get or set whether or not the cursor is enabled. - virtual bool GetCursorEnabled() const = 0; - virtual void SetCursorEnabled(bool enabled) = 0; - - // Following methods are to forward key/focus related events to the - // views wrapper so that TextfieldViews can handle key inputs without - // having focus. - - // Invoked when a key is pressed/release on Textfield. Subclasser - // should return true if the event has been processed and false - // otherwise. - // See also View::OnKeyPressed/OnKeyReleased. - virtual bool HandleKeyPressed(const ui::KeyEvent& e) = 0; - virtual bool HandleKeyReleased(const ui::KeyEvent& e) = 0; - - // Invoked when focus is being moved from or to the Textfield. - // See also View::OnFocus/OnBlur. - virtual void HandleFocus() = 0; - virtual void HandleBlur() = 0; - - // Returns the View's TextInputClient instance or NULL if the View doesn't - // support text input. - virtual ui::TextInputClient* GetTextInputClient() = 0; - - // Set the text colors; see the corresponding Textfield functions for details. - virtual void SetColor(SkColor value) = 0; - virtual void ApplyColor(SkColor value, const gfx::Range& range) = 0; - - // Set the text styles; see the corresponding Textfield functions for details. - virtual void SetStyle(gfx::TextStyle style, bool value) = 0; - virtual void ApplyStyle(gfx::TextStyle style, - bool value, - const gfx::Range& range) = 0; - - // Clears Edit history. - virtual void ClearEditHistory() = 0; - - // Get the height in pixels of the first font used in this textfield. - virtual int GetFontHeight() = 0; - - // Returns the baseline of the textfield. This should not take into account - // any insets. - virtual int GetTextfieldBaseline() const = 0; - - // Returns the width necessary to display the current text, including any - // necessary space for the cursor or border/margin. - virtual int GetWidthNeededForText() const = 0; - - // Performs the action associated with the specified command id. Not called - // ExecuteCommand to avoid name clash. - virtual void ExecuteTextCommand(int command_id) = 0; - - // Returns whether there is a drag operation originating from the textfield. - virtual bool HasTextBeingDragged() = 0; - - // Returns the location for keyboard-triggered context menus. - virtual gfx::Point GetContextMenuLocation() = 0; - - // Creates an appropriate NativeTextfieldWrapper for the platform. - static NativeTextfieldWrapper* CreateWrapper(Textfield* field); -}; - -} // namespace views - -#endif // UI_VIEWS_CONTROLS_TEXTFIELD_NATIVE_TEXTFIELD_WRAPPER_H_ diff --git a/chromium/ui/views/controls/textfield/textfield.cc b/chromium/ui/views/controls/textfield/textfield.cc index de590dc304c..f28f8f27e18 100644 --- a/chromium/ui/views/controls/textfield/textfield.cc +++ b/chromium/ui/views/controls/textfield/textfield.cc @@ -6,40 +6,235 @@ #include <string> -#include "base/command_line.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "ui/base/accessibility/accessible_view_state.h" -#include "ui/base/ime/text_input_type.h" +#include "base/debug/trace_event.h" +#include "grit/ui_strings.h" +#include "ui/accessibility/ax_view_state.h" +#include "ui/base/clipboard/scoped_clipboard_writer.h" +#include "ui/base/cursor/cursor.h" +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/dragdrop/drag_utils.h" #include "ui/base/resource/resource_bundle.h" -#include "ui/base/ui_base_switches.h" +#include "ui/base/ui_base_switches_util.h" +#include "ui/compositor/scoped_animation_duration_scale_mode.h" #include "ui/events/event.h" #include "ui/events/keycodes/keyboard_codes.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/display.h" #include "ui/gfx/insets.h" -#include "ui/gfx/range/range.h" -#include "ui/gfx/selection_model.h" +#include "ui/gfx/screen.h" #include "ui/native_theme/native_theme.h" +#include "ui/views/background.h" +#include "ui/views/controls/focusable_border.h" +#include "ui/views/controls/label.h" +#include "ui/views/controls/menu/menu_runner.h" #include "ui/views/controls/native/native_view_host.h" -#include "ui/views/controls/textfield/native_textfield_views.h" -#include "ui/views/controls/textfield/native_textfield_wrapper.h" #include "ui/views/controls/textfield/textfield_controller.h" +#include "ui/views/drag_utils.h" +#include "ui/views/ime/input_method.h" +#include "ui/views/metrics.h" +#include "ui/views/native_cursor.h" #include "ui/views/painter.h" #include "ui/views/views_delegate.h" #include "ui/views/widget/widget.h" +#if defined(OS_WIN) +#include "base/win/win_util.h" +#endif + +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) +#include "base/strings/utf_string_conversions.h" +#include "ui/events/linux/text_edit_command_auralinux.h" +#include "ui/events/linux/text_edit_key_bindings_delegate_auralinux.h" +#endif + +namespace views { + namespace { // Default placeholder text color. const SkColor kDefaultPlaceholderTextColor = SK_ColorLTGRAY; -gfx::FontList GetDefaultFontList() { - return ResourceBundle::GetSharedInstance().GetFontList( - ResourceBundle::BaseFont); +const int kNoCommand = 0; + +void ConvertRectToScreen(const View* src, gfx::Rect* r) { + DCHECK(src); + + gfx::Point new_origin = r->origin(); + View::ConvertPointToScreen(src, &new_origin); + r->set_origin(new_origin); } -} // namespace +// Get the drag selection timer delay, respecting animation scaling for testing. +int GetDragSelectionDelay() { + switch (ui::ScopedAnimationDurationScaleMode::duration_scale_mode()) { + case ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION: return 100; + case ui::ScopedAnimationDurationScaleMode::FAST_DURATION: return 25; + case ui::ScopedAnimationDurationScaleMode::SLOW_DURATION: return 400; + case ui::ScopedAnimationDurationScaleMode::ZERO_DURATION: return 0; + } + return 100; +} + +// Get the default command for a given key |event| and selection state. +int GetCommandForKeyEvent(const ui::KeyEvent& event, bool has_selection) { + if (event.type() != ui::ET_KEY_PRESSED || event.IsUnicodeKeyCode()) + return kNoCommand; + + const bool shift = event.IsShiftDown(); + const bool control = event.IsControlDown(); + const bool alt = event.IsAltDown() || event.IsAltGrDown(); + switch (event.key_code()) { + case ui::VKEY_Z: + if (control && !shift && !alt) + return IDS_APP_UNDO; + return (control && shift && !alt) ? IDS_APP_REDO : kNoCommand; + case ui::VKEY_Y: + return (control && !alt) ? IDS_APP_REDO : kNoCommand; + case ui::VKEY_A: + return (control && !alt) ? IDS_APP_SELECT_ALL : kNoCommand; + case ui::VKEY_X: + return (control && !alt) ? IDS_APP_CUT : kNoCommand; + case ui::VKEY_C: + return (control && !alt) ? IDS_APP_COPY : kNoCommand; + case ui::VKEY_V: + return (control && !alt) ? IDS_APP_PASTE : kNoCommand; + case ui::VKEY_RIGHT: + // Ignore alt+right, which may be a browser navigation shortcut. + if (alt) + return kNoCommand; + if (!shift) + return control ? IDS_MOVE_WORD_RIGHT : IDS_MOVE_RIGHT; + return control ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION : + IDS_MOVE_RIGHT_AND_MODIFY_SELECTION; + case ui::VKEY_LEFT: + // Ignore alt+left, which may be a browser navigation shortcut. + if (alt) + return kNoCommand; + if (!shift) + return control ? IDS_MOVE_WORD_LEFT : IDS_MOVE_LEFT; + return control ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION : + IDS_MOVE_LEFT_AND_MODIFY_SELECTION; + case ui::VKEY_HOME: + return shift ? IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION : + IDS_MOVE_TO_BEGINNING_OF_LINE; + case ui::VKEY_END: + return shift ? IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION : + IDS_MOVE_TO_END_OF_LINE; + case ui::VKEY_BACK: + if (!control || has_selection) + return IDS_DELETE_BACKWARD; +#if defined(OS_LINUX) + // Only erase by line break on Linux and ChromeOS. + if (shift) + return IDS_DELETE_TO_BEGINNING_OF_LINE; +#endif + return IDS_DELETE_WORD_BACKWARD; + case ui::VKEY_DELETE: + if (!control || has_selection) + return (shift && has_selection) ? IDS_APP_CUT : IDS_DELETE_FORWARD; +#if defined(OS_LINUX) + // Only erase by line break on Linux and ChromeOS. + if (shift) + return IDS_DELETE_TO_END_OF_LINE; +#endif + return IDS_DELETE_WORD_FORWARD; + case ui::VKEY_INSERT: + if (control && !shift) + return IDS_APP_COPY; + return (shift && !control) ? IDS_APP_PASTE : kNoCommand; + default: + return kNoCommand; + } +} -namespace views { +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) +// Convert a custom text edit |command| to the equivalent views command ID. +int GetViewsCommand(const ui::TextEditCommandAuraLinux& command, bool rtl) { + const bool select = command.extend_selection(); + switch (command.command_id()) { + case ui::TextEditCommandAuraLinux::COPY: + return IDS_APP_COPY; + case ui::TextEditCommandAuraLinux::CUT: + return IDS_APP_CUT; + case ui::TextEditCommandAuraLinux::DELETE_BACKWARD: + return IDS_DELETE_BACKWARD; + case ui::TextEditCommandAuraLinux::DELETE_FORWARD: + return IDS_DELETE_FORWARD; + case ui::TextEditCommandAuraLinux::DELETE_TO_BEGINING_OF_LINE: + case ui::TextEditCommandAuraLinux::DELETE_TO_BEGINING_OF_PARAGRAPH: + return IDS_DELETE_TO_BEGINNING_OF_LINE; + case ui::TextEditCommandAuraLinux::DELETE_TO_END_OF_LINE: + case ui::TextEditCommandAuraLinux::DELETE_TO_END_OF_PARAGRAPH: + return IDS_DELETE_TO_END_OF_LINE; + case ui::TextEditCommandAuraLinux::DELETE_WORD_BACKWARD: + return IDS_DELETE_WORD_BACKWARD; + case ui::TextEditCommandAuraLinux::DELETE_WORD_FORWARD: + return IDS_DELETE_WORD_FORWARD; + case ui::TextEditCommandAuraLinux::INSERT_TEXT: + return kNoCommand; + case ui::TextEditCommandAuraLinux::MOVE_BACKWARD: + if (rtl) + return select ? IDS_MOVE_RIGHT_AND_MODIFY_SELECTION : IDS_MOVE_RIGHT; + return select ? IDS_MOVE_LEFT_AND_MODIFY_SELECTION : IDS_MOVE_LEFT; + case ui::TextEditCommandAuraLinux::MOVE_DOWN: + return IDS_MOVE_DOWN; + case ui::TextEditCommandAuraLinux::MOVE_FORWARD: + if (rtl) + return select ? IDS_MOVE_LEFT_AND_MODIFY_SELECTION : IDS_MOVE_LEFT; + return select ? IDS_MOVE_RIGHT_AND_MODIFY_SELECTION : IDS_MOVE_RIGHT; + case ui::TextEditCommandAuraLinux::MOVE_LEFT: + return select ? IDS_MOVE_LEFT_AND_MODIFY_SELECTION : IDS_MOVE_LEFT; + case ui::TextEditCommandAuraLinux::MOVE_PAGE_DOWN: + case ui::TextEditCommandAuraLinux::MOVE_PAGE_UP: + return kNoCommand; + case ui::TextEditCommandAuraLinux::MOVE_RIGHT: + return select ? IDS_MOVE_RIGHT_AND_MODIFY_SELECTION : IDS_MOVE_RIGHT; + case ui::TextEditCommandAuraLinux::MOVE_TO_BEGINING_OF_DOCUMENT: + case ui::TextEditCommandAuraLinux::MOVE_TO_BEGINING_OF_LINE: + case ui::TextEditCommandAuraLinux::MOVE_TO_BEGINING_OF_PARAGRAPH: + return select ? IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION : + IDS_MOVE_TO_BEGINNING_OF_LINE; + case ui::TextEditCommandAuraLinux::MOVE_TO_END_OF_DOCUMENT: + case ui::TextEditCommandAuraLinux::MOVE_TO_END_OF_LINE: + case ui::TextEditCommandAuraLinux::MOVE_TO_END_OF_PARAGRAPH: + return select ? IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION : + IDS_MOVE_TO_END_OF_LINE; + case ui::TextEditCommandAuraLinux::MOVE_UP: + return IDS_MOVE_UP; + case ui::TextEditCommandAuraLinux::MOVE_WORD_BACKWARD: + if (rtl) { + return select ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION : + IDS_MOVE_WORD_RIGHT; + } + return select ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION : + IDS_MOVE_WORD_LEFT; + case ui::TextEditCommandAuraLinux::MOVE_WORD_FORWARD: + if (rtl) { + return select ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION : + IDS_MOVE_WORD_LEFT; + } + return select ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION : + IDS_MOVE_WORD_RIGHT; + case ui::TextEditCommandAuraLinux::MOVE_WORD_LEFT: + return select ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION : + IDS_MOVE_WORD_LEFT; + case ui::TextEditCommandAuraLinux::MOVE_WORD_RIGHT: + return select ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION : + IDS_MOVE_WORD_RIGHT; + case ui::TextEditCommandAuraLinux::PASTE: + return IDS_APP_PASTE; + case ui::TextEditCommandAuraLinux::SELECT_ALL: + return IDS_APP_SELECT_ALL; + case ui::TextEditCommandAuraLinux::SET_MARK: + case ui::TextEditCommandAuraLinux::UNSELECT: + case ui::TextEditCommandAuraLinux::INVALID_COMMAND: + return kNoCommand; + } + return kNoCommand; +} +#endif + +} // namespace // static const char Textfield::kViewClassName[] = "Textfield"; @@ -56,153 +251,102 @@ size_t Textfield::GetCaretBlinkMs() { } Textfield::Textfield() - : native_wrapper_(NULL), + : model_(new TextfieldModel(this)), controller_(NULL), - style_(STYLE_DEFAULT), - font_list_(GetDefaultFontList()), read_only_(false), default_width_in_chars_(0), - draw_border_(true), - text_color_(SK_ColorBLACK), use_default_text_color_(true), - background_color_(SK_ColorWHITE), use_default_background_color_(true), - horizontal_margins_were_set_(false), - vertical_margins_were_set_(false), - placeholder_text_color_(kDefaultPlaceholderTextColor), - text_input_type_(ui::TEXT_INPUT_TYPE_TEXT), - weak_ptr_factory_(this) { - SetFocusable(true); - - if (ViewsDelegate::views_delegate) { - obscured_reveal_duration_ = ViewsDelegate::views_delegate-> - GetDefaultTextfieldObscuredRevealDuration(); - } - - if (NativeViewHost::kRenderNativeControlFocus) - focus_painter_ = Painter::CreateDashedFocusPainter(); -} - -Textfield::Textfield(StyleFlags style) - : native_wrapper_(NULL), - controller_(NULL), - style_(style), - font_list_(GetDefaultFontList()), - read_only_(false), - default_width_in_chars_(0), - draw_border_(true), + use_default_selection_text_color_(true), + use_default_selection_background_color_(true), text_color_(SK_ColorBLACK), - use_default_text_color_(true), background_color_(SK_ColorWHITE), - use_default_background_color_(true), - horizontal_margins_were_set_(false), - vertical_margins_were_set_(false), + selection_text_color_(SK_ColorWHITE), + selection_background_color_(SK_ColorBLUE), placeholder_text_color_(kDefaultPlaceholderTextColor), text_input_type_(ui::TEXT_INPUT_TYPE_TEXT), + performing_user_action_(false), + skip_input_method_cancel_composition_(false), + cursor_visible_(false), + drop_cursor_visible_(false), + initiating_drag_(false), + aggregated_clicks_(0), weak_ptr_factory_(this) { + set_context_menu_controller(this); + set_drag_controller(this); + SetBorder(scoped_ptr<Border>(new FocusableBorder())); SetFocusable(true); - if (IsObscured()) - SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); if (ViewsDelegate::views_delegate) { - obscured_reveal_duration_ = ViewsDelegate::views_delegate-> + password_reveal_duration_ = ViewsDelegate::views_delegate-> GetDefaultTextfieldObscuredRevealDuration(); } - - if (NativeViewHost::kRenderNativeControlFocus) - focus_painter_ = Painter::CreateDashedFocusPainter(); -} - -Textfield::~Textfield() { -} - -void Textfield::SetController(TextfieldController* controller) { - controller_ = controller; } -TextfieldController* Textfield::GetController() const { - return controller_; -} +Textfield::~Textfield() {} void Textfield::SetReadOnly(bool read_only) { // Update read-only without changing the focusable state (or active, etc.). read_only_ = read_only; - if (native_wrapper_) { - native_wrapper_->UpdateReadOnly(); - native_wrapper_->UpdateTextColor(); - native_wrapper_->UpdateBackgroundColor(); - } -} - -bool Textfield::IsObscured() const { - return style_ & STYLE_OBSCURED; -} - -void Textfield::SetObscured(bool obscured) { - if (obscured) { - style_ = static_cast<StyleFlags>(style_ | STYLE_OBSCURED); - SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); - } else { - style_ = static_cast<StyleFlags>(style_ & ~STYLE_OBSCURED); - SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT); - } - if (native_wrapper_) - native_wrapper_->UpdateIsObscured(); -} - -ui::TextInputType Textfield::GetTextInputType() const { - if (read_only() || !enabled()) - return ui::TEXT_INPUT_TYPE_NONE; - return text_input_type_; + if (GetInputMethod()) + GetInputMethod()->OnTextInputTypeChanged(this); + SetColor(GetTextColor()); + UpdateBackgroundColor(); } void Textfield::SetTextInputType(ui::TextInputType type) { + GetRenderText()->SetObscured(type == ui::TEXT_INPUT_TYPE_PASSWORD); text_input_type_ = type; - bool should_be_obscured = type == ui::TEXT_INPUT_TYPE_PASSWORD; - if (IsObscured() != should_be_obscured) - SetObscured(should_be_obscured); + OnCaretBoundsChanged(); + if (GetInputMethod()) + GetInputMethod()->OnTextInputTypeChanged(this); + SchedulePaint(); } -void Textfield::SetText(const string16& text) { - text_ = text; - if (native_wrapper_) - native_wrapper_->UpdateText(); +void Textfield::SetText(const base::string16& new_text) { + model_->SetText(new_text); + OnCaretBoundsChanged(); + SchedulePaint(); + NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_CHANGED, true); } -void Textfield::AppendText(const string16& text) { - text_ += text; - if (native_wrapper_) - native_wrapper_->AppendText(text); +void Textfield::AppendText(const base::string16& new_text) { + if (new_text.empty()) + return; + model_->Append(new_text); + OnCaretBoundsChanged(); + SchedulePaint(); } -void Textfield::InsertOrReplaceText(const string16& text) { - if (native_wrapper_) { - native_wrapper_->InsertOrReplaceText(text); - text_ = native_wrapper_->GetText(); - } +void Textfield::InsertOrReplaceText(const base::string16& new_text) { + if (new_text.empty()) + return; + model_->InsertText(new_text); + OnCaretBoundsChanged(); + SchedulePaint(); } base::i18n::TextDirection Textfield::GetTextDirection() const { - return native_wrapper_ ? - native_wrapper_->GetTextDirection() : base::i18n::UNKNOWN_DIRECTION; + return GetRenderText()->GetTextDirection(); } -void Textfield::SelectAll(bool reversed) { - if (native_wrapper_) - native_wrapper_->SelectAll(reversed); +base::string16 Textfield::GetSelectedText() const { + return model_->GetSelectedText(); } -string16 Textfield::GetSelectedText() const { - return native_wrapper_ ? native_wrapper_->GetSelectedText() : string16(); +void Textfield::SelectAll(bool reversed) { + model_->SelectAll(reversed); + UpdateSelectionClipboard(); + UpdateAfterChange(false, true); } -void Textfield::ClearSelection() const { - if (native_wrapper_) - native_wrapper_->ClearSelection(); +void Textfield::ClearSelection() { + model_->ClearSelection(); + UpdateAfterChange(false, true); } bool Textfield::HasSelection() const { - return native_wrapper_ && !native_wrapper_->GetSelectedRange().is_empty(); + return !GetSelectedRange().is_empty(); } SkColor Textfield::GetTextColor() const { @@ -217,14 +361,12 @@ SkColor Textfield::GetTextColor() const { void Textfield::SetTextColor(SkColor color) { text_color_ = color; use_default_text_color_ = false; - if (native_wrapper_) - native_wrapper_->UpdateTextColor(); + SetColor(color); } void Textfield::UseDefaultTextColor() { use_default_text_color_ = true; - if (native_wrapper_) - native_wrapper_->UpdateTextColor(); + SetColor(GetTextColor()); } SkColor Textfield::GetBackgroundColor() const { @@ -239,173 +381,149 @@ SkColor Textfield::GetBackgroundColor() const { void Textfield::SetBackgroundColor(SkColor color) { background_color_ = color; use_default_background_color_ = false; - if (native_wrapper_) - native_wrapper_->UpdateBackgroundColor(); + UpdateBackgroundColor(); } void Textfield::UseDefaultBackgroundColor() { use_default_background_color_ = true; - if (native_wrapper_) - native_wrapper_->UpdateBackgroundColor(); + UpdateBackgroundColor(); } -bool Textfield::GetCursorEnabled() const { - return native_wrapper_ && native_wrapper_->GetCursorEnabled(); +SkColor Textfield::GetSelectionTextColor() const { + return use_default_selection_text_color_ ? + GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_TextfieldSelectionColor) : + selection_text_color_; } -void Textfield::SetCursorEnabled(bool enabled) { - if (native_wrapper_) - native_wrapper_->SetCursorEnabled(enabled); +void Textfield::SetSelectionTextColor(SkColor color) { + selection_text_color_ = color; + use_default_selection_text_color_ = false; + GetRenderText()->set_selection_color(GetSelectionTextColor()); + SchedulePaint(); } -void Textfield::SetFontList(const gfx::FontList& font_list) { - font_list_ = font_list; - if (native_wrapper_) - native_wrapper_->UpdateFont(); - PreferredSizeChanged(); +void Textfield::UseDefaultSelectionTextColor() { + use_default_selection_text_color_ = true; + GetRenderText()->set_selection_color(GetSelectionTextColor()); + SchedulePaint(); } -const gfx::Font& Textfield::GetPrimaryFont() const { - return font_list_.GetPrimaryFont(); +SkColor Textfield::GetSelectionBackgroundColor() const { + return use_default_selection_background_color_ ? + GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused) : + selection_background_color_; } -void Textfield::SetFont(const gfx::Font& font) { - SetFontList(gfx::FontList(font)); +void Textfield::SetSelectionBackgroundColor(SkColor color) { + selection_background_color_ = color; + use_default_selection_background_color_ = false; + GetRenderText()->set_selection_background_focused_color( + GetSelectionBackgroundColor()); + SchedulePaint(); } -void Textfield::SetHorizontalMargins(int left, int right) { - if (horizontal_margins_were_set_ && - left == margins_.left() && right == margins_.right()) { - return; - } - margins_.Set(margins_.top(), left, margins_.bottom(), right); - horizontal_margins_were_set_ = true; - if (native_wrapper_) - native_wrapper_->UpdateHorizontalMargins(); - PreferredSizeChanged(); +void Textfield::UseDefaultSelectionBackgroundColor() { + use_default_selection_background_color_ = true; + GetRenderText()->set_selection_background_focused_color( + GetSelectionBackgroundColor()); + SchedulePaint(); } -void Textfield::SetVerticalMargins(int top, int bottom) { - if (vertical_margins_were_set_ && - top == margins_.top() && bottom == margins_.bottom()) { - return; - } - margins_.Set(top, margins_.left(), bottom, margins_.right()); - vertical_margins_were_set_ = true; - if (native_wrapper_) - native_wrapper_->UpdateVerticalMargins(); - PreferredSizeChanged(); +bool Textfield::GetCursorEnabled() const { + return GetRenderText()->cursor_enabled(); } -void Textfield::RemoveBorder() { - if (!draw_border_) - return; +void Textfield::SetCursorEnabled(bool enabled) { + GetRenderText()->SetCursorEnabled(enabled); +} + +const gfx::FontList& Textfield::GetFontList() const { + return GetRenderText()->font_list(); +} - draw_border_ = false; - if (native_wrapper_) - native_wrapper_->UpdateBorder(); +void Textfield::SetFontList(const gfx::FontList& font_list) { + GetRenderText()->SetFontList(font_list); + OnCaretBoundsChanged(); + PreferredSizeChanged(); } base::string16 Textfield::GetPlaceholderText() const { return placeholder_text_; } -bool Textfield::GetHorizontalMargins(int* left, int* right) { - if (!horizontal_margins_were_set_) - return false; - - *left = margins_.left(); - *right = margins_.right(); - return true; +gfx::HorizontalAlignment Textfield::GetHorizontalAlignment() const { + return GetRenderText()->horizontal_alignment(); } -bool Textfield::GetVerticalMargins(int* top, int* bottom) { - if (!vertical_margins_were_set_) - return false; - - *top = margins_.top(); - *bottom = margins_.bottom(); - return true; +void Textfield::SetHorizontalAlignment(gfx::HorizontalAlignment alignment) { + GetRenderText()->SetHorizontalAlignment(alignment); } -void Textfield::UpdateAllProperties() { - if (native_wrapper_) { - native_wrapper_->UpdateText(); - native_wrapper_->UpdateTextColor(); - native_wrapper_->UpdateBackgroundColor(); - native_wrapper_->UpdateReadOnly(); - native_wrapper_->UpdateFont(); - native_wrapper_->UpdateEnabled(); - native_wrapper_->UpdateBorder(); - native_wrapper_->UpdateIsObscured(); - native_wrapper_->UpdateHorizontalMargins(); - native_wrapper_->UpdateVerticalMargins(); - } -} - -void Textfield::SyncText() { - if (native_wrapper_) { - string16 new_text = native_wrapper_->GetText(); - if (new_text != text_) { - text_ = new_text; - if (controller_) - controller_->ContentsChanged(this, text_); - } - } +void Textfield::ShowImeIfNeeded() { + if (enabled() && !read_only()) + GetInputMethod()->ShowImeIfNeeded(); } bool Textfield::IsIMEComposing() const { - return native_wrapper_ && native_wrapper_->IsIMEComposing(); + return model_->HasCompositionText(); } -gfx::Range Textfield::GetSelectedRange() const { - return native_wrapper_->GetSelectedRange(); +const gfx::Range& Textfield::GetSelectedRange() const { + return GetRenderText()->selection(); } void Textfield::SelectRange(const gfx::Range& range) { - native_wrapper_->SelectRange(range); + model_->SelectRange(range); + UpdateAfterChange(false, true); } -gfx::SelectionModel Textfield::GetSelectionModel() const { - return native_wrapper_->GetSelectionModel(); +const gfx::SelectionModel& Textfield::GetSelectionModel() const { + return GetRenderText()->selection_model(); } void Textfield::SelectSelectionModel(const gfx::SelectionModel& sel) { - native_wrapper_->SelectSelectionModel(sel); + model_->SelectSelectionModel(sel); + UpdateAfterChange(false, true); } size_t Textfield::GetCursorPosition() const { - return native_wrapper_->GetCursorPosition(); + return model_->GetCursorPosition(); } void Textfield::SetColor(SkColor value) { - return native_wrapper_->SetColor(value); + GetRenderText()->SetColor(value); + SchedulePaint(); } void Textfield::ApplyColor(SkColor value, const gfx::Range& range) { - return native_wrapper_->ApplyColor(value, range); + GetRenderText()->ApplyColor(value, range); + SchedulePaint(); } void Textfield::SetStyle(gfx::TextStyle style, bool value) { - return native_wrapper_->SetStyle(style, value); + GetRenderText()->SetStyle(style, value); + SchedulePaint(); } void Textfield::ApplyStyle(gfx::TextStyle style, bool value, const gfx::Range& range) { - return native_wrapper_->ApplyStyle(style, value, range); + GetRenderText()->ApplyStyle(style, value, range); + SchedulePaint(); } void Textfield::ClearEditHistory() { - native_wrapper_->ClearEditHistory(); + model_->ClearEditHistory(); } -void Textfield::SetAccessibleName(const string16& name) { +void Textfield::SetAccessibleName(const base::string16& name) { accessible_name_ = name; } void Textfield::ExecuteCommand(int command_id) { - native_wrapper_->ExecuteTextCommand(command_id); + ExecuteCommand(command_id, ui::EF_NONE); } void Textfield::SetFocusPainter(scoped_ptr<Painter> focus_painter) { @@ -413,101 +531,360 @@ void Textfield::SetFocusPainter(scoped_ptr<Painter> focus_painter) { } bool Textfield::HasTextBeingDragged() { - return native_wrapper_->HasTextBeingDragged(); + return initiating_drag_; } //////////////////////////////////////////////////////////////////////////////// // Textfield, View overrides: -void Textfield::Layout() { - if (native_wrapper_) { - native_wrapper_->GetView()->SetBoundsRect(GetContentsBounds()); - native_wrapper_->GetView()->Layout(); - } +int Textfield::GetBaseline() const { + return GetInsets().top() + GetRenderText()->GetBaseline(); } -int Textfield::GetBaseline() const { - gfx::Insets insets = GetTextInsets(); - const int baseline = native_wrapper_ ? - native_wrapper_->GetTextfieldBaseline() : font_list_.GetBaseline(); - return insets.top() + baseline; +gfx::Size Textfield::GetPreferredSize() const { + const gfx::Insets& insets = GetInsets(); + return gfx::Size(GetFontList().GetExpectedTextWidth(default_width_in_chars_) + + insets.width(), GetFontList().GetHeight() + insets.height()); } -gfx::Size Textfield::GetPreferredSize() { - gfx::Insets insets = GetTextInsets(); +const char* Textfield::GetClassName() const { + return kViewClassName; +} - const int font_height = native_wrapper_ ? native_wrapper_->GetFontHeight() : - font_list_.GetHeight(); - return gfx::Size( - GetPrimaryFont().GetExpectedTextWidth(default_width_in_chars_) - + insets.width(), - font_height + insets.height()); +gfx::NativeCursor Textfield::GetCursor(const ui::MouseEvent& event) { + bool in_selection = GetRenderText()->IsPointInSelection(event.location()); + bool drag_event = event.type() == ui::ET_MOUSE_DRAGGED; + bool text_cursor = !initiating_drag_ && (drag_event || !in_selection); + return text_cursor ? GetNativeIBeamCursor() : gfx::kNullCursor; } -void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse) { - SelectAll(false); +bool Textfield::OnMousePressed(const ui::MouseEvent& event) { + TrackMouseClicks(event); + + if (!controller_ || !controller_->HandleMouseEvent(this, event)) { + if (event.IsOnlyLeftMouseButton() || event.IsOnlyRightMouseButton()) { + RequestFocus(); + ShowImeIfNeeded(); + } + + if (event.IsOnlyLeftMouseButton()) { + OnBeforeUserAction(); + initiating_drag_ = false; + switch (aggregated_clicks_) { + case 0: + if (GetRenderText()->IsPointInSelection(event.location())) + initiating_drag_ = true; + else + MoveCursorTo(event.location(), event.IsShiftDown()); + break; + case 1: + model_->MoveCursorTo(event.location(), false); + model_->SelectWord(); + UpdateAfterChange(false, true); + double_click_word_ = GetRenderText()->selection(); + break; + case 2: + SelectAll(false); + break; + default: + NOTREACHED(); + } + OnAfterUserAction(); + } + +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + if (event.IsOnlyMiddleMouseButton()) { + if (GetRenderText()->IsPointInSelection(event.location())) { + OnBeforeUserAction(); + ClearSelection(); + ui::ScopedClipboardWriter( + ui::Clipboard::GetForCurrentThread(), + ui::CLIPBOARD_TYPE_SELECTION).WriteText(base::string16()); + OnAfterUserAction(); + } else if(!read_only()) { + PasteSelectionClipboard(event); + } + } +#endif + } + + return true; } -bool Textfield::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) { - // Skip any accelerator handling of backspace; textfields handle this key. - // Also skip processing of [Alt]+<num-pad digit> Unicode alt key codes. - return e.key_code() == ui::VKEY_BACK || e.IsUnicodeKeyCode(); +bool Textfield::OnMouseDragged(const ui::MouseEvent& event) { + last_drag_location_ = event.location(); + + // Don't adjust the cursor on a potential drag and drop, or if the mouse + // movement from the last mouse click does not exceed the drag threshold. + if (initiating_drag_ || !event.IsOnlyLeftMouseButton() || + !ExceededDragThreshold(last_drag_location_ - last_click_location_)) { + return true; + } + + // A timer is used to continuously scroll while selecting beyond side edges. + if ((event.location().x() > 0 && event.location().x() < size().width()) || + GetDragSelectionDelay() == 0) { + drag_selection_timer_.Stop(); + SelectThroughLastDragLocation(); + } else if (!drag_selection_timer_.IsRunning()) { + drag_selection_timer_.Start( + FROM_HERE, base::TimeDelta::FromMilliseconds(GetDragSelectionDelay()), + this, &Textfield::SelectThroughLastDragLocation); + } + + return true; } -void Textfield::OnPaint(gfx::Canvas* canvas) { - View::OnPaint(canvas); - if (NativeViewHost::kRenderNativeControlFocus) - Painter::PaintFocusPainter(this, canvas, focus_painter_.get()); +void Textfield::OnMouseReleased(const ui::MouseEvent& event) { + OnBeforeUserAction(); + drag_selection_timer_.Stop(); + // Cancel suspected drag initiations, the user was clicking in the selection. + if (initiating_drag_) + MoveCursorTo(event.location(), false); + initiating_drag_ = false; + UpdateSelectionClipboard(); + OnAfterUserAction(); +} + +bool Textfield::OnKeyPressed(const ui::KeyEvent& event) { + bool handled = controller_ && controller_->HandleKeyEvent(this, event); + +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + ui::TextEditKeyBindingsDelegateAuraLinux* delegate = + ui::GetTextEditKeyBindingsDelegate(); + std::vector<ui::TextEditCommandAuraLinux> commands; + if (!handled && delegate && delegate->MatchEvent(event, &commands)) { + const bool rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT; + for (size_t i = 0; i < commands.size(); ++i) { + const int command = GetViewsCommand(commands[i], rtl); + if (IsCommandIdEnabled(command)) { + ExecuteCommand(command); + handled = true; + } + } + return handled; + } +#endif + + const int command = GetCommandForKeyEvent(event, HasSelection()); + if (!handled && IsCommandIdEnabled(command)) { + ExecuteCommand(command); + handled = true; + } + return handled; +} + +ui::TextInputClient* Textfield::GetTextInputClient() { + return read_only_ ? NULL : this; +} + +void Textfield::OnGestureEvent(ui::GestureEvent* event) { + switch (event->type()) { + case ui::ET_GESTURE_TAP_DOWN: + OnBeforeUserAction(); + RequestFocus(); + ShowImeIfNeeded(); + + // We don't deselect if the point is in the selection + // because TAP_DOWN may turn into a LONG_PRESS. + if (!GetRenderText()->IsPointInSelection(event->location())) + MoveCursorTo(event->location(), false); + OnAfterUserAction(); + event->SetHandled(); + break; + case ui::ET_GESTURE_SCROLL_UPDATE: + OnBeforeUserAction(); + MoveCursorTo(event->location(), true); + OnAfterUserAction(); + event->SetHandled(); + break; + case ui::ET_GESTURE_SCROLL_END: + case ui::ET_SCROLL_FLING_START: + CreateTouchSelectionControllerAndNotifyIt(); + event->SetHandled(); + break; + case ui::ET_GESTURE_TAP: + if (event->details().tap_count() == 1) { + CreateTouchSelectionControllerAndNotifyIt(); + } else { + DestroyTouchSelection(); + OnBeforeUserAction(); + SelectAll(false); + OnAfterUserAction(); + event->SetHandled(); + } +#if defined(OS_WIN) + if (!read_only()) + base::win::DisplayVirtualKeyboard(); +#endif + break; + case ui::ET_GESTURE_LONG_PRESS: + // If long press happens outside selection, select word and show context + // menu (If touch selection is enabled, context menu is shown by the + // |touch_selection_controller_|, hence we mark the event handled. + // Otherwise, the regular context menu will be shown by views). + // If long press happens in selected text and touch drag drop is enabled, + // we will turn off touch selection (if one exists) and let views do drag + // drop. + if (!GetRenderText()->IsPointInSelection(event->location())) { + OnBeforeUserAction(); + model_->SelectWord(); + touch_selection_controller_.reset( + ui::TouchSelectionController::create(this)); + UpdateAfterChange(false, true); + OnAfterUserAction(); + if (touch_selection_controller_) + event->SetHandled(); + } else if (switches::IsTouchDragDropEnabled()) { + initiating_drag_ = true; + DestroyTouchSelection(); + } else { + if (!touch_selection_controller_) + CreateTouchSelectionControllerAndNotifyIt(); + if (touch_selection_controller_) + event->SetHandled(); + } + return; + case ui::ET_GESTURE_LONG_TAP: + if (!touch_selection_controller_) + CreateTouchSelectionControllerAndNotifyIt(); + + // If touch selection is enabled, the context menu on long tap will be + // shown by the |touch_selection_controller_|, hence we mark the event + // handled so views does not try to show context menu on it. + if (touch_selection_controller_) + event->SetHandled(); + break; + default: + return; + } } -bool Textfield::OnKeyPressed(const ui::KeyEvent& e) { - return native_wrapper_ && native_wrapper_->HandleKeyPressed(e); +void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse) { + SelectAll(false); } -bool Textfield::OnKeyReleased(const ui::KeyEvent& e) { - return native_wrapper_ && native_wrapper_->HandleKeyReleased(e); +bool Textfield::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) { +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + // Skip any accelerator handling that conflicts with custom keybindings. + ui::TextEditKeyBindingsDelegateAuraLinux* delegate = + ui::GetTextEditKeyBindingsDelegate(); + std::vector<ui::TextEditCommandAuraLinux> commands; + if (delegate && delegate->MatchEvent(event, &commands)) { + const bool rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT; + for (size_t i = 0; i < commands.size(); ++i) + if (IsCommandIdEnabled(GetViewsCommand(commands[i], rtl))) + return true; + } +#endif + + // Skip backspace accelerator handling; editable textfields handle this key. + // Also skip processing Windows [Alt]+<num-pad digit> Unicode alt-codes. + const bool is_backspace = event.key_code() == ui::VKEY_BACK; + return (is_backspace && !read_only()) || event.IsUnicodeKeyCode(); } -bool Textfield::OnMouseDragged(const ui::MouseEvent& e) { - if (!e.IsOnlyRightMouseButton()) - return View::OnMouseDragged(e); +bool Textfield::GetDropFormats( + int* formats, + std::set<OSExchangeData::CustomFormat>* custom_formats) { + if (!enabled() || read_only()) + return false; + // TODO(msw): Can we support URL, FILENAME, etc.? + *formats = ui::OSExchangeData::STRING; + if (controller_) + controller_->AppendDropFormats(formats, custom_formats); return true; } -void Textfield::OnFocus() { - if (native_wrapper_) - native_wrapper_->HandleFocus(); +bool Textfield::CanDrop(const OSExchangeData& data) { + int formats; + std::set<OSExchangeData::CustomFormat> custom_formats; + GetDropFormats(&formats, &custom_formats); + return enabled() && !read_only() && + data.HasAnyFormat(formats, custom_formats); +} + +int Textfield::OnDragUpdated(const ui::DropTargetEvent& event) { + DCHECK(CanDrop(event.data())); + gfx::RenderText* render_text = GetRenderText(); + const gfx::Range& selection = render_text->selection(); + drop_cursor_position_ = render_text->FindCursorPosition(event.location()); + bool in_selection = !selection.is_empty() && + selection.Contains(gfx::Range(drop_cursor_position_.caret_pos())); + drop_cursor_visible_ = !in_selection; + // TODO(msw): Pan over text when the user drags to the visible text edge. + OnCaretBoundsChanged(); + SchedulePaint(); - // Forward the focus to the wrapper if it exists. - if (!native_wrapper_ || !native_wrapper_->SetFocus()) { - // If there is no wrapper or the wrapper didn't take focus, call - // View::Focus to clear the native focus so that we still get - // keyboard messages. - View::OnFocus(); + if (initiating_drag_) { + if (in_selection) + return ui::DragDropTypes::DRAG_NONE; + return event.IsControlDown() ? ui::DragDropTypes::DRAG_COPY : + ui::DragDropTypes::DRAG_MOVE; } + return ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE; +} - // Border typically draws focus indicator. +void Textfield::OnDragExited() { + drop_cursor_visible_ = false; SchedulePaint(); } -void Textfield::OnBlur() { - if (native_wrapper_) - native_wrapper_->HandleBlur(); +int Textfield::OnPerformDrop(const ui::DropTargetEvent& event) { + DCHECK(CanDrop(event.data())); + drop_cursor_visible_ = false; - // Border typically draws focus indicator. - SchedulePaint(); + if (controller_) { + int drag_operation = controller_->OnDrop(event.data()); + if (drag_operation != ui::DragDropTypes::DRAG_NONE) + return drag_operation; + } + + gfx::RenderText* render_text = GetRenderText(); + DCHECK(!initiating_drag_ || + !render_text->IsPointInSelection(event.location())); + OnBeforeUserAction(); + skip_input_method_cancel_composition_ = true; + + gfx::SelectionModel drop_destination_model = + render_text->FindCursorPosition(event.location()); + base::string16 new_text; + event.data().GetString(&new_text); + + // Delete the current selection for a drag and drop within this view. + const bool move = initiating_drag_ && !event.IsControlDown() && + event.source_operations() & ui::DragDropTypes::DRAG_MOVE; + if (move) { + // Adjust the drop destination if it is on or after the current selection. + size_t pos = drop_destination_model.caret_pos(); + pos -= render_text->selection().Intersect(gfx::Range(0, pos)).length(); + model_->DeleteSelectionAndInsertTextAt(new_text, pos); + } else { + model_->MoveCursorTo(drop_destination_model); + // Drop always inserts text even if the textfield is not in insert mode. + model_->InsertText(new_text); + } + skip_input_method_cancel_composition_ = false; + UpdateAfterChange(true, true); + OnAfterUserAction(); + return move ? ui::DragDropTypes::DRAG_MOVE : ui::DragDropTypes::DRAG_COPY; +} + +void Textfield::OnDragDone() { + initiating_drag_ = false; + drop_cursor_visible_ = false; } -void Textfield::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_TEXT; +void Textfield::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_TEXT_FIELD; state->name = accessible_name_; if (read_only()) - state->state |= ui::AccessibilityTypes::STATE_READONLY; - if (IsObscured()) - state->state |= ui::AccessibilityTypes::STATE_PROTECTED; - state->value = text_; + state->AddStateFlag(ui::AX_STATE_READ_ONLY); + if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD) + state->AddStateFlag(ui::AX_STATE_PROTECTED); + state->value = text(); - const gfx::Range range = native_wrapper_->GetSelectedRange(); + const gfx::Range range = GetSelectedRange(); state->selection_start = range.start(); state->selection_end = range.end(); @@ -518,61 +895,881 @@ void Textfield::GetAccessibleState(ui::AccessibleViewState* state) { } } -ui::TextInputClient* Textfield::GetTextInputClient() { - return native_wrapper_ ? native_wrapper_->GetTextInputClient() : NULL; +void Textfield::OnBoundsChanged(const gfx::Rect& previous_bounds) { + GetRenderText()->SetDisplayRect(GetContentsBounds()); + OnCaretBoundsChanged(); +} + +void Textfield::OnEnabledChanged() { + View::OnEnabledChanged(); + if (GetInputMethod()) + GetInputMethod()->OnTextInputTypeChanged(this); + SchedulePaint(); +} + +void Textfield::OnPaint(gfx::Canvas* canvas) { + OnPaintBackground(canvas); + PaintTextAndCursor(canvas); + OnPaintBorder(canvas); +} + +void Textfield::OnFocus() { + GetRenderText()->set_focused(true); + cursor_visible_ = true; + SchedulePaint(); + GetInputMethod()->OnFocus(); + OnCaretBoundsChanged(); + + const size_t caret_blink_ms = Textfield::GetCaretBlinkMs(); + if (caret_blink_ms != 0) { + cursor_repaint_timer_.Start(FROM_HERE, + base::TimeDelta::FromMilliseconds(caret_blink_ms), this, + &Textfield::UpdateCursor); + } + + View::OnFocus(); + SchedulePaint(); +} + +void Textfield::OnBlur() { + GetRenderText()->set_focused(false); + GetInputMethod()->OnBlur(); + cursor_repaint_timer_.Stop(); + if (cursor_visible_) { + cursor_visible_ = false; + RepaintCursor(); + } + + DestroyTouchSelection(); + + // Border typically draws focus indicator. + SchedulePaint(); } gfx::Point Textfield::GetKeyboardContextMenuLocation() { - return native_wrapper_ ? native_wrapper_->GetContextMenuLocation() : - View::GetKeyboardContextMenuLocation(); + return GetCaretBounds().bottom_right(); } -void Textfield::OnEnabledChanged() { - View::OnEnabledChanged(); - if (native_wrapper_) - native_wrapper_->UpdateEnabled(); +void Textfield::OnNativeThemeChanged(const ui::NativeTheme* theme) { + gfx::RenderText* render_text = GetRenderText(); + render_text->SetColor(GetTextColor()); + UpdateBackgroundColor(); + render_text->set_cursor_color(GetTextColor()); + render_text->set_selection_color(GetSelectionTextColor()); + render_text->set_selection_background_focused_color( + GetSelectionBackgroundColor()); +} + +//////////////////////////////////////////////////////////////////////////////// +// Textfield, TextfieldModel::Delegate overrides: + +void Textfield::OnCompositionTextConfirmedOrCleared() { + if (!skip_input_method_cancel_composition_) + GetInputMethod()->CancelComposition(this); +} + +//////////////////////////////////////////////////////////////////////////////// +// Textfield, ContextMenuController overrides: + +void Textfield::ShowContextMenuForView(View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) { + UpdateContextMenu(); + ignore_result(context_menu_runner_->RunMenuAt( + GetWidget(), + NULL, + gfx::Rect(point, gfx::Size()), + MENU_ANCHOR_TOPLEFT, + source_type, + MenuRunner::HAS_MNEMONICS | MenuRunner::CONTEXT_MENU)); +} + +//////////////////////////////////////////////////////////////////////////////// +// Textfield, DragController overrides: + +void Textfield::WriteDragDataForView(View* sender, + const gfx::Point& press_pt, + OSExchangeData* data) { + const base::string16& selected_text(GetSelectedText()); + data->SetString(selected_text); + Label label(selected_text, GetFontList()); + label.SetBackgroundColor(GetBackgroundColor()); + label.set_subpixel_rendering_enabled(false); + gfx::Size size(label.GetPreferredSize()); + gfx::NativeView native_view = GetWidget()->GetNativeView(); + gfx::Display display = gfx::Screen::GetScreenFor(native_view)-> + GetDisplayNearestWindow(native_view); + size.SetToMin(gfx::Size(display.size().width(), height())); + label.SetBoundsRect(gfx::Rect(size)); + scoped_ptr<gfx::Canvas> canvas( + GetCanvasForDragImage(GetWidget(), label.size())); + label.SetEnabledColor(GetTextColor()); +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + // Desktop Linux Aura does not yet support transparency in drag images. + canvas->DrawColor(GetBackgroundColor()); +#endif + label.Paint(canvas.get(), views::CullSet()); + const gfx::Vector2d kOffset(-15, 0); + drag_utils::SetDragImageOnDataObject(*canvas, kOffset, data); + if (controller_) + controller_->OnWriteDragData(data); +} + +int Textfield::GetDragOperationsForView(View* sender, const gfx::Point& p) { + int drag_operations = ui::DragDropTypes::DRAG_COPY; + if (!enabled() || text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD || + !GetRenderText()->IsPointInSelection(p)) { + drag_operations = ui::DragDropTypes::DRAG_NONE; + } else if (sender == this && !read_only()) { + drag_operations = + ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY; + } + if (controller_) + controller_->OnGetDragOperationsForTextfield(&drag_operations); + return drag_operations; } -void Textfield::ViewHierarchyChanged( - const ViewHierarchyChangedDetails& details) { - if (details.is_add && !native_wrapper_ && GetWidget()) { - // The native wrapper's lifetime will be managed by the view hierarchy after - // we call AddChildView. - native_wrapper_ = NativeTextfieldWrapper::CreateWrapper(this); - AddChildViewAt(native_wrapper_->GetView(), 0); - Layout(); - UpdateAllProperties(); +bool Textfield::CanStartDragForView(View* sender, + const gfx::Point& press_pt, + const gfx::Point& p) { + return initiating_drag_ && GetRenderText()->IsPointInSelection(press_pt); +} + +//////////////////////////////////////////////////////////////////////////////// +// Textfield, ui::TouchEditable overrides: + +void Textfield::SelectRect(const gfx::Point& start, const gfx::Point& end) { + if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) + return; + + gfx::SelectionModel start_caret = GetRenderText()->FindCursorPosition(start); + gfx::SelectionModel end_caret = GetRenderText()->FindCursorPosition(end); + gfx::SelectionModel selection( + gfx::Range(start_caret.caret_pos(), end_caret.caret_pos()), + end_caret.caret_affinity()); + + OnBeforeUserAction(); + SelectSelectionModel(selection); + OnAfterUserAction(); +} + +void Textfield::MoveCaretTo(const gfx::Point& point) { + SelectRect(point, point); +} + +void Textfield::GetSelectionEndPoints(gfx::Rect* p1, gfx::Rect* p2) { + gfx::RenderText* render_text = GetRenderText(); + const gfx::SelectionModel& sel = render_text->selection_model(); + gfx::SelectionModel start_sel = + render_text->GetSelectionModelForSelectionStart(); + *p1 = render_text->GetCursorBounds(start_sel, true); + *p2 = render_text->GetCursorBounds(sel, true); +} + +gfx::Rect Textfield::GetBounds() { + return GetLocalBounds(); +} + +gfx::NativeView Textfield::GetNativeView() const { + return GetWidget()->GetNativeView(); +} + +void Textfield::ConvertPointToScreen(gfx::Point* point) { + View::ConvertPointToScreen(this, point); +} + +void Textfield::ConvertPointFromScreen(gfx::Point* point) { + View::ConvertPointFromScreen(this, point); +} + +bool Textfield::DrawsHandles() { + return false; +} + +void Textfield::OpenContextMenu(const gfx::Point& anchor) { + DestroyTouchSelection(); + ShowContextMenu(anchor, ui::MENU_SOURCE_TOUCH_EDIT_MENU); +} + +void Textfield::DestroyTouchSelection() { + touch_selection_controller_.reset(); +} + +//////////////////////////////////////////////////////////////////////////////// +// Textfield, ui::SimpleMenuModel::Delegate overrides: + +bool Textfield::IsCommandIdChecked(int command_id) const { + return true; +} + +bool Textfield::IsCommandIdEnabled(int command_id) const { + base::string16 result; + bool editable = !read_only(); + bool readable = text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD; + switch (command_id) { + case IDS_APP_UNDO: + return editable && model_->CanUndo(); + case IDS_APP_REDO: + return editable && model_->CanRedo(); + case IDS_APP_CUT: + return editable && readable && model_->HasSelection(); + case IDS_APP_COPY: + return readable && model_->HasSelection(); + case IDS_APP_PASTE: + ui::Clipboard::GetForCurrentThread()->ReadText( + ui::CLIPBOARD_TYPE_COPY_PASTE, &result); + return editable && !result.empty(); + case IDS_APP_DELETE: + return editable && model_->HasSelection(); + case IDS_APP_SELECT_ALL: + return !text().empty(); + case IDS_DELETE_FORWARD: + case IDS_DELETE_BACKWARD: + case IDS_DELETE_TO_BEGINNING_OF_LINE: + case IDS_DELETE_TO_END_OF_LINE: + case IDS_DELETE_WORD_BACKWARD: + case IDS_DELETE_WORD_FORWARD: + return editable; + case IDS_MOVE_LEFT: + case IDS_MOVE_LEFT_AND_MODIFY_SELECTION: + case IDS_MOVE_RIGHT: + case IDS_MOVE_RIGHT_AND_MODIFY_SELECTION: + case IDS_MOVE_WORD_LEFT: + case IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION: + case IDS_MOVE_WORD_RIGHT: + case IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION: + case IDS_MOVE_TO_BEGINNING_OF_LINE: + case IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION: + case IDS_MOVE_TO_END_OF_LINE: + case IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION: + return true; + default: + return false; } } -const char* Textfield::GetClassName() const { - return kViewClassName; +bool Textfield::GetAcceleratorForCommandId(int command_id, + ui::Accelerator* accelerator) { + return false; +} + +void Textfield::ExecuteCommand(int command_id, int event_flags) { + DestroyTouchSelection(); + if (!IsCommandIdEnabled(command_id)) + return; + + bool text_changed = false; + bool cursor_changed = false; + bool rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT; + gfx::VisualCursorDirection begin = rtl ? gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT; + gfx::VisualCursorDirection end = rtl ? gfx::CURSOR_LEFT : gfx::CURSOR_RIGHT; + gfx::SelectionModel selection_model = GetSelectionModel(); + + OnBeforeUserAction(); + switch (command_id) { + case IDS_APP_UNDO: + text_changed = cursor_changed = model_->Undo(); + break; + case IDS_APP_REDO: + text_changed = cursor_changed = model_->Redo(); + break; + case IDS_APP_CUT: + text_changed = cursor_changed = Cut(); + break; + case IDS_APP_COPY: + Copy(); + break; + case IDS_APP_PASTE: + text_changed = cursor_changed = Paste(); + break; + case IDS_APP_DELETE: + text_changed = cursor_changed = model_->Delete(); + break; + case IDS_APP_SELECT_ALL: + SelectAll(false); + break; + case IDS_DELETE_BACKWARD: + text_changed = cursor_changed = model_->Backspace(); + break; + case IDS_DELETE_FORWARD: + text_changed = cursor_changed = model_->Delete(); + break; + case IDS_DELETE_TO_END_OF_LINE: + model_->MoveCursor(gfx::LINE_BREAK, end, true); + text_changed = cursor_changed = model_->Delete(); + break; + case IDS_DELETE_TO_BEGINNING_OF_LINE: + model_->MoveCursor(gfx::LINE_BREAK, begin, true); + text_changed = cursor_changed = model_->Backspace(); + break; + case IDS_DELETE_WORD_BACKWARD: + model_->MoveCursor(gfx::WORD_BREAK, begin, true); + text_changed = cursor_changed = model_->Backspace(); + break; + case IDS_DELETE_WORD_FORWARD: + model_->MoveCursor(gfx::WORD_BREAK, end, true); + text_changed = cursor_changed = model_->Delete(); + break; + case IDS_MOVE_LEFT: + model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, false); + break; + case IDS_MOVE_LEFT_AND_MODIFY_SELECTION: + model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); + break; + case IDS_MOVE_RIGHT: + model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); + break; + case IDS_MOVE_RIGHT_AND_MODIFY_SELECTION: + model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); + break; + case IDS_MOVE_WORD_LEFT: + model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, false); + break; + case IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION: + model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true); + break; + case IDS_MOVE_WORD_RIGHT: + model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, false); + break; + case IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION: + model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true); + break; + case IDS_MOVE_TO_BEGINNING_OF_LINE: + model_->MoveCursor(gfx::LINE_BREAK, begin, false); + break; + case IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION: + model_->MoveCursor(gfx::LINE_BREAK, begin, true); + break; + case IDS_MOVE_TO_END_OF_LINE: + model_->MoveCursor(gfx::LINE_BREAK, end, false); + break; + case IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION: + model_->MoveCursor(gfx::LINE_BREAK, end, true); + break; + default: + NOTREACHED(); + break; + } + + cursor_changed |= GetSelectionModel() != selection_model; + if (cursor_changed) + UpdateSelectionClipboard(); + UpdateAfterChange(text_changed, cursor_changed); + OnAfterUserAction(); } //////////////////////////////////////////////////////////////////////////////// -// Textfield, private: +// Textfield, ui::TextInputClient overrides: + +void Textfield::SetCompositionText(const ui::CompositionText& composition) { + if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) + return; + + OnBeforeUserAction(); + skip_input_method_cancel_composition_ = true; + model_->SetCompositionText(composition); + skip_input_method_cancel_composition_ = false; + UpdateAfterChange(true, true); + OnAfterUserAction(); +} + +void Textfield::ConfirmCompositionText() { + if (!model_->HasCompositionText()) + return; + + OnBeforeUserAction(); + skip_input_method_cancel_composition_ = true; + model_->ConfirmCompositionText(); + skip_input_method_cancel_composition_ = false; + UpdateAfterChange(true, true); + OnAfterUserAction(); +} + +void Textfield::ClearCompositionText() { + if (!model_->HasCompositionText()) + return; + + OnBeforeUserAction(); + skip_input_method_cancel_composition_ = true; + model_->CancelCompositionText(); + skip_input_method_cancel_composition_ = false; + UpdateAfterChange(true, true); + OnAfterUserAction(); +} + +void Textfield::InsertText(const base::string16& new_text) { + // TODO(suzhe): Filter invalid characters. + if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || new_text.empty()) + return; + + OnBeforeUserAction(); + skip_input_method_cancel_composition_ = true; + if (GetRenderText()->insert_mode()) + model_->InsertText(new_text); + else + model_->ReplaceText(new_text); + skip_input_method_cancel_composition_ = false; + UpdateAfterChange(true, true); + OnAfterUserAction(); +} + +void Textfield::InsertChar(base::char16 ch, int flags) { + const int kControlModifierMask = ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | + ui::EF_COMMAND_DOWN | ui::EF_ALTGR_DOWN | + ui::EF_MOD3_DOWN; + + // Filter out all control characters, including tab and new line characters, + // and all characters with Alt modifier. But allow characters with the AltGr + // modifier. On Windows AltGr is represented by Alt+Ctrl, and on Linux it's a + // different flag that we don't care about. + const bool should_insert_char = + ((ch >= 0x20 && ch < 0x7F) || ch > 0x9F) && + (flags & kControlModifierMask) != ui::EF_ALT_DOWN; + if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || !should_insert_char) + return; + + OnBeforeUserAction(); + skip_input_method_cancel_composition_ = true; + if (GetRenderText()->insert_mode()) + model_->InsertChar(ch); + else + model_->ReplaceChar(ch); + skip_input_method_cancel_composition_ = false; + + UpdateAfterChange(true, true); + OnAfterUserAction(); + + if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD && + password_reveal_duration_ != base::TimeDelta()) { + const size_t change_offset = model_->GetCursorPosition(); + DCHECK_GT(change_offset, 0u); + RevealPasswordChar(change_offset - 1); + } +} + +gfx::NativeWindow Textfield::GetAttachedWindow() const { + // Imagine the following hierarchy. + // [NativeWidget A] - FocusManager + // [View] + // [NativeWidget B] + // [View] + // [View X] + // An important thing is that [NativeWidget A] owns Win32 input focus even + // when [View X] is logically focused by FocusManager. As a result, an Win32 + // IME may want to interact with the native view of [NativeWidget A] rather + // than that of [NativeWidget B]. This is why we need to call + // GetTopLevelWidget() here. + return GetWidget()->GetTopLevelWidget()->GetNativeWindow(); +} + +ui::TextInputType Textfield::GetTextInputType() const { + if (read_only() || !enabled()) + return ui::TEXT_INPUT_TYPE_NONE; + return text_input_type_; +} + +ui::TextInputMode Textfield::GetTextInputMode() const { + return ui::TEXT_INPUT_MODE_DEFAULT; +} + +bool Textfield::CanComposeInline() const { + return true; +} + +gfx::Rect Textfield::GetCaretBounds() const { + gfx::Rect rect = GetRenderText()->GetUpdatedCursorBounds(); + ConvertRectToScreen(this, &rect); + return rect; +} + +bool Textfield::GetCompositionCharacterBounds(uint32 index, + gfx::Rect* rect) const { + DCHECK(rect); + if (!HasCompositionText()) + return false; + gfx::RenderText* render_text = GetRenderText(); + const gfx::Range& composition_range = render_text->GetCompositionRange(); + DCHECK(!composition_range.is_empty()); + + size_t text_index = composition_range.start() + index; + if (composition_range.end() <= text_index) + return false; + if (!render_text->IsValidCursorIndex(text_index)) { + text_index = render_text->IndexOfAdjacentGrapheme( + text_index, gfx::CURSOR_BACKWARD); + } + if (text_index < composition_range.start()) + return false; + const gfx::SelectionModel caret(text_index, gfx::CURSOR_BACKWARD); + *rect = render_text->GetCursorBounds(caret, false); + ConvertRectToScreen(this, rect); + return true; +} + +bool Textfield::HasCompositionText() const { + return model_->HasCompositionText(); +} + +bool Textfield::GetTextRange(gfx::Range* range) const { + if (!ImeEditingAllowed()) + return false; + + model_->GetTextRange(range); + return true; +} + +bool Textfield::GetCompositionTextRange(gfx::Range* range) const { + if (!ImeEditingAllowed()) + return false; + + model_->GetCompositionTextRange(range); + return true; +} -gfx::Insets Textfield::GetTextInsets() const { - gfx::Insets insets = GetInsets(); - if (draw_border_ && native_wrapper_) - insets += native_wrapper_->CalculateInsets(); - return insets; +bool Textfield::GetSelectionRange(gfx::Range* range) const { + if (!ImeEditingAllowed()) + return false; + *range = GetRenderText()->selection(); + return true; +} + +bool Textfield::SetSelectionRange(const gfx::Range& range) { + if (!ImeEditingAllowed() || !range.IsValid()) + return false; + OnBeforeUserAction(); + SelectRange(range); + OnAfterUserAction(); + return true; +} + +bool Textfield::DeleteRange(const gfx::Range& range) { + if (!ImeEditingAllowed() || range.is_empty()) + return false; + + OnBeforeUserAction(); + model_->SelectRange(range); + if (model_->HasSelection()) { + model_->DeleteSelection(); + UpdateAfterChange(true, true); + } + OnAfterUserAction(); + return true; +} + +bool Textfield::GetTextFromRange(const gfx::Range& range, + base::string16* range_text) const { + if (!ImeEditingAllowed() || !range.IsValid()) + return false; + + gfx::Range text_range; + if (!GetTextRange(&text_range) || !text_range.Contains(range)) + return false; + + *range_text = model_->GetTextFromRange(range); + return true; +} + +void Textfield::OnInputMethodChanged() {} + +bool Textfield::ChangeTextDirectionAndLayoutAlignment( + base::i18n::TextDirection direction) { + // Restore text directionality mode when the indicated direction matches the + // current forced mode; otherwise, force the mode indicated. This helps users + // manage BiDi text layout without getting stuck in forced LTR or RTL modes. + const gfx::DirectionalityMode mode = direction == base::i18n::RIGHT_TO_LEFT ? + gfx::DIRECTIONALITY_FORCE_RTL : gfx::DIRECTIONALITY_FORCE_LTR; + if (mode == GetRenderText()->directionality_mode()) + GetRenderText()->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_TEXT); + else + GetRenderText()->SetDirectionalityMode(mode); + SchedulePaint(); + return true; } -void Textfield::AccessibilitySetValue(const string16& new_value) { +void Textfield::ExtendSelectionAndDelete(size_t before, size_t after) { + gfx::Range range = GetRenderText()->selection(); + DCHECK_GE(range.start(), before); + + range.set_start(range.start() - before); + range.set_end(range.end() + after); + gfx::Range text_range; + if (GetTextRange(&text_range) && text_range.Contains(range)) + DeleteRange(range); +} + +void Textfield::EnsureCaretInRect(const gfx::Rect& rect) {} + +void Textfield::OnCandidateWindowShown() {} + +void Textfield::OnCandidateWindowUpdated() {} + +void Textfield::OnCandidateWindowHidden() {} + +bool Textfield::IsEditingCommandEnabled(int command_id) { + return IsCommandIdEnabled(command_id); +} + +void Textfield::ExecuteEditingCommand(int command_id) { + ExecuteCommand(command_id); +} + +//////////////////////////////////////////////////////////////////////////////// +// Textfield, protected: + +gfx::RenderText* Textfield::GetRenderText() const { + return model_->render_text(); +} + +base::string16 Textfield::GetSelectionClipboardText() const { + base::string16 selection_clipboard_text; + ui::Clipboard::GetForCurrentThread()->ReadText( + ui::CLIPBOARD_TYPE_SELECTION, &selection_clipboard_text); + return selection_clipboard_text; +} + +//////////////////////////////////////////////////////////////////////////////// +// Textfield, private: + +void Textfield::AccessibilitySetValue(const base::string16& new_value) { if (!read_only()) { SetText(new_value); ClearSelection(); } } -//////////////////////////////////////////////////////////////////////////////// -// NativeTextfieldWrapper, public: +void Textfield::UpdateBackgroundColor() { + const SkColor color = GetBackgroundColor(); + set_background(Background::CreateSolidBackground(color)); + GetRenderText()->set_background_is_transparent(SkColorGetA(color) != 0xFF); + SchedulePaint(); +} -// static -NativeTextfieldWrapper* NativeTextfieldWrapper::CreateWrapper( - Textfield* field) { - return new NativeTextfieldViews(field); +void Textfield::UpdateAfterChange(bool text_changed, bool cursor_changed) { + if (text_changed) { + if (controller_) + controller_->ContentsChanged(this, text()); + NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_CHANGED, true); + } + if (cursor_changed) { + cursor_visible_ = true; + RepaintCursor(); + if (cursor_repaint_timer_.IsRunning()) + cursor_repaint_timer_.Reset(); + if (!text_changed) { + // TEXT_CHANGED implies SELECTION_CHANGED, so we only need to fire + // this if only the selection changed. + NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION_CHANGED, true); + } + } + if (text_changed || cursor_changed) { + OnCaretBoundsChanged(); + SchedulePaint(); + } +} + +void Textfield::UpdateCursor() { + const size_t caret_blink_ms = Textfield::GetCaretBlinkMs(); + cursor_visible_ = !cursor_visible_ || (caret_blink_ms == 0); + RepaintCursor(); +} + +void Textfield::RepaintCursor() { + gfx::Rect r(GetRenderText()->GetUpdatedCursorBounds()); + r.Inset(-1, -1, -1, -1); + SchedulePaintInRect(r); +} + +void Textfield::PaintTextAndCursor(gfx::Canvas* canvas) { + TRACE_EVENT0("views", "Textfield::PaintTextAndCursor"); + canvas->Save(); + + // Draw placeholder text if needed. + gfx::RenderText* render_text = GetRenderText(); + if (text().empty() && !GetPlaceholderText().empty()) { + canvas->DrawStringRect(GetPlaceholderText(), GetFontList(), + placeholder_text_color(), render_text->display_rect()); + } + + // Draw the text, cursor, and selection. + render_text->set_cursor_visible(cursor_visible_ && !drop_cursor_visible_ && + !HasSelection()); + render_text->Draw(canvas); + + // Draw the detached drop cursor that marks where the text will be dropped. + if (drop_cursor_visible_) + render_text->DrawCursor(canvas, drop_cursor_position_); + + canvas->Restore(); +} + +void Textfield::MoveCursorTo(const gfx::Point& point, bool select) { + if (model_->MoveCursorTo(point, select)) + UpdateAfterChange(false, true); +} + +void Textfield::SelectThroughLastDragLocation() { + OnBeforeUserAction(); + model_->MoveCursorTo(last_drag_location_, true); + if (aggregated_clicks_ == 1) { + model_->SelectWord(); + // Expand the selection so the initially selected word remains selected. + gfx::Range selection = GetRenderText()->selection(); + const size_t min = std::min(selection.GetMin(), + double_click_word_.GetMin()); + const size_t max = std::max(selection.GetMax(), + double_click_word_.GetMax()); + const bool reversed = selection.is_reversed(); + selection.set_start(reversed ? max : min); + selection.set_end(reversed ? min : max); + model_->SelectRange(selection); + } + UpdateAfterChange(false, true); + OnAfterUserAction(); +} + +void Textfield::OnCaretBoundsChanged() { + if (GetInputMethod()) + GetInputMethod()->OnCaretBoundsChanged(this); + if (touch_selection_controller_) + touch_selection_controller_->SelectionChanged(); +} + +void Textfield::OnBeforeUserAction() { + DCHECK(!performing_user_action_); + performing_user_action_ = true; + if (controller_) + controller_->OnBeforeUserAction(this); +} + +void Textfield::OnAfterUserAction() { + if (controller_) + controller_->OnAfterUserAction(this); + DCHECK(performing_user_action_); + performing_user_action_ = false; +} + +bool Textfield::Cut() { + if (!read_only() && text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD && + model_->Cut()) { + if (controller_) + controller_->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_COPY_PASTE); + return true; + } + return false; +} + +bool Textfield::Copy() { + if (text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD && model_->Copy()) { + if (controller_) + controller_->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_COPY_PASTE); + return true; + } + return false; +} + +bool Textfield::Paste() { + if (!read_only() && model_->Paste()) { + if (controller_) + controller_->OnAfterPaste(); + return true; + } + return false; +} + +void Textfield::UpdateContextMenu() { + if (!context_menu_contents_.get()) { + context_menu_contents_.reset(new ui::SimpleMenuModel(this)); + context_menu_contents_->AddItemWithStringId(IDS_APP_UNDO, IDS_APP_UNDO); + context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR); + context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT); + context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY); + context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE); + context_menu_contents_->AddItemWithStringId(IDS_APP_DELETE, IDS_APP_DELETE); + context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR); + context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL, + IDS_APP_SELECT_ALL); + if (controller_) + controller_->UpdateContextMenu(context_menu_contents_.get()); + } + context_menu_runner_.reset(new MenuRunner(context_menu_contents_.get())); +} + +void Textfield::TrackMouseClicks(const ui::MouseEvent& event) { + if (event.IsOnlyLeftMouseButton()) { + base::TimeDelta time_delta = event.time_stamp() - last_click_time_; + if (time_delta.InMilliseconds() <= GetDoubleClickInterval() && + !ExceededDragThreshold(event.location() - last_click_location_)) { + // Upon clicking after a triple click, the count should go back to double + // click and alternate between double and triple. This assignment maps + // 0 to 1, 1 to 2, 2 to 1. + aggregated_clicks_ = (aggregated_clicks_ % 2) + 1; + } else { + aggregated_clicks_ = 0; + } + last_click_time_ = event.time_stamp(); + last_click_location_ = event.location(); + } +} + +bool Textfield::ImeEditingAllowed() const { + // Disallow input method editing of password fields. + ui::TextInputType t = GetTextInputType(); + return (t != ui::TEXT_INPUT_TYPE_NONE && t != ui::TEXT_INPUT_TYPE_PASSWORD); +} + +void Textfield::RevealPasswordChar(int index) { + GetRenderText()->SetObscuredRevealIndex(index); + SchedulePaint(); + + if (index != -1) { + password_reveal_timer_.Start(FROM_HERE, password_reveal_duration_, + base::Bind(&Textfield::RevealPasswordChar, + weak_ptr_factory_.GetWeakPtr(), -1)); + } +} + +void Textfield::CreateTouchSelectionControllerAndNotifyIt() { + if (!touch_selection_controller_) { + touch_selection_controller_.reset( + ui::TouchSelectionController::create(this)); + } + if (touch_selection_controller_) + touch_selection_controller_->SelectionChanged(); +} + +void Textfield::UpdateSelectionClipboard() const { +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + if (performing_user_action_ && HasSelection()) { + ui::ScopedClipboardWriter( + ui::Clipboard::GetForCurrentThread(), + ui::CLIPBOARD_TYPE_SELECTION).WriteText(GetSelectedText()); + if (controller_) + controller_->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_SELECTION); + } +#endif +} + +void Textfield::PasteSelectionClipboard(const ui::MouseEvent& event) { + DCHECK(event.IsOnlyMiddleMouseButton()); + DCHECK(!read_only()); + base::string16 selection_clipboard_text = GetSelectionClipboardText(); + if (!selection_clipboard_text.empty()) { + OnBeforeUserAction(); + gfx::Range range = GetSelectionModel().selection(); + gfx::LogicalCursorDirection affinity = GetSelectionModel().caret_affinity(); + const gfx::SelectionModel mouse = + GetRenderText()->FindCursorPosition(event.location()); + model_->MoveCursorTo(mouse); + model_->InsertText(selection_clipboard_text); + // Update the new selection range as needed. + if (range.GetMin() >= mouse.caret_pos()) { + const size_t length = selection_clipboard_text.length(); + range = gfx::Range(range.start() + length, range.end() + length); + } + model_->MoveCursorTo(gfx::SelectionModel(range, affinity)); + UpdateAfterChange(true, true); + OnAfterUserAction(); + } } } // namespace views diff --git a/chromium/ui/views/controls/textfield/textfield.h b/chromium/ui/views/controls/textfield/textfield.h index 307e562c20e..e8361774a94 100644 --- a/chromium/ui/views/controls/textfield/textfield.h +++ b/chromium/ui/views/controls/textfield/textfield.h @@ -12,103 +12,77 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/strings/string16.h" -#include "base/time/time.h" -#include "build/build_config.h" +#include "base/timer/timer.h" #include "third_party/skia/include/core/SkColor.h" +#include "ui/base/ime/text_input_client.h" #include "ui/base/ime/text_input_type.h" +#include "ui/base/models/simple_menu_model.h" +#include "ui/base/touch/touch_editing_controller.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/font_list.h" -#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/range/range.h" +#include "ui/gfx/selection_model.h" #include "ui/gfx/text_constants.h" -#include "ui/views/controls/textfield/native_textfield_wrapper.h" +#include "ui/views/context_menu_controller.h" +#include "ui/views/controls/textfield/textfield_model.h" +#include "ui/views/drag_controller.h" #include "ui/views/view.h" -#if !defined(OS_LINUX) -#include "base/logging.h" -#endif - -namespace gfx { -class Range; -class ImageSkia; -} - -namespace ui { -class TextInputClient; -} // namespace ui - namespace views { -class ImageView; +class MenuRunner; class Painter; class TextfieldController; -// This class implements a View that wraps a native text (edit) field. -class VIEWS_EXPORT Textfield : public View { +// A views/skia textfield implementation. No platform-specific code is used. +class VIEWS_EXPORT Textfield : public View, + public TextfieldModel::Delegate, + public ContextMenuController, + public DragController, + public ui::TouchEditable, + public ui::TextInputClient { public: // The textfield's class name. static const char kViewClassName[]; - enum StyleFlags { - STYLE_DEFAULT = 0, - STYLE_OBSCURED = 1 << 0, - STYLE_LOWERCASE = 1 << 1 - }; - // Returns the text cursor blink time in milliseconds, or 0 for no blinking. static size_t GetCaretBlinkMs(); Textfield(); - explicit Textfield(StyleFlags style); virtual ~Textfield(); - // TextfieldController accessors - void SetController(TextfieldController* controller); - TextfieldController* GetController() const; + // Set the controller for this textfield. + void set_controller(TextfieldController* controller) { + controller_ = controller; + } // Gets/Sets whether or not the Textfield is read-only. bool read_only() const { return read_only_; } void SetReadOnly(bool read_only); - // Gets/sets the STYLE_OBSCURED bit, controlling whether characters in this - // Textfield are displayed as asterisks/bullets. - bool IsObscured() const; - void SetObscured(bool obscured); - - // Gets/sets the duration to reveal the last typed char when the obscured bit - // is set. A duration of zero effectively disables the feature. Other values - // cause the last typed char to be shown for the defined duration. Note this - // only works with NativeTextfieldViews. - const base::TimeDelta& obscured_reveal_duration() const { - return obscured_reveal_duration_; - } - void set_obscured_reveal_duration(const base::TimeDelta& duration) { - obscured_reveal_duration_ = duration; - } - - // Gets/Sets the input type of this textfield. - ui::TextInputType GetTextInputType() const; + // Sets the input type; displays only asterisks for TEXT_INPUT_TYPE_PASSWORD. void SetTextInputType(ui::TextInputType type); - // Gets/Sets the text currently displayed in the Textfield. - const string16& text() const { return text_; } + // Gets the text currently displayed in the Textfield. + const base::string16& text() const { return model_->text(); } // Sets the text currently displayed in the Textfield. This doesn't // change the cursor position if the current cursor is within the // new text's range, or moves the cursor to the end if the cursor is // out of the new text's range. - void SetText(const string16& text); + void SetText(const base::string16& new_text); // Appends the given string to the previously-existing text in the field. - void AppendText(const string16& text); + void AppendText(const base::string16& new_text); - // Inserts |text| at the current cursor position, replacing any selected text. - void InsertOrReplaceText(const string16& text); + // Inserts |new_text| at the cursor position, replacing any selected text. + void InsertOrReplaceText(const base::string16& new_text); // Returns the text direction. base::i18n::TextDirection GetTextDirection() const; // Returns the text that is currently selected. - string16 GetSelectedText() const; + base::string16 GetSelectedText() const; // Select the entire text range. If |reversed| is true, the range will end at // the logical beginning of the text; this generally shows the leading portion @@ -116,56 +90,51 @@ class VIEWS_EXPORT Textfield : public View { void SelectAll(bool reversed); // Clears the selection within the edit field and sets the caret to the end. - void ClearSelection() const; + void ClearSelection(); // Checks if there is any selected text. bool HasSelection() const; - // Accessor for |style_|. - StyleFlags style() const { return style_; } - - // Gets/Sets the text color to be used when painting the Textfield. - // Call |UseDefaultTextColor| to restore the default system color. + // Gets/sets the text color to be used when painting the Textfield. + // Call UseDefaultTextColor() to restore the default system color. SkColor GetTextColor() const; void SetTextColor(SkColor color); void UseDefaultTextColor(); - // Gets/Sets the background color to be used when painting the Textfield. - // Call |UseDefaultBackgroundColor| to restore the default system color. + // Gets/sets the background color to be used when painting the Textfield. + // Call UseDefaultBackgroundColor() to restore the default system color. SkColor GetBackgroundColor() const; void SetBackgroundColor(SkColor color); void UseDefaultBackgroundColor(); + // Gets/sets the selection text color to be used when painting the Textfield. + // Call UseDefaultSelectionTextColor() to restore the default system color. + SkColor GetSelectionTextColor() const; + void SetSelectionTextColor(SkColor color); + void UseDefaultSelectionTextColor(); + + // Gets/sets the selection background color to be used when painting the + // Textfield. Call UseDefaultSelectionBackgroundColor() to restore the default + // system color. + SkColor GetSelectionBackgroundColor() const; + void SetSelectionBackgroundColor(SkColor color); + void UseDefaultSelectionBackgroundColor(); + // Gets/Sets whether or not the cursor is enabled. bool GetCursorEnabled() const; void SetCursorEnabled(bool enabled); // Gets/Sets the fonts used when rendering the text within the Textfield. - const gfx::FontList& font_list() const { return font_list_; } + const gfx::FontList& GetFontList() const; void SetFontList(const gfx::FontList& font_list); - const gfx::Font& GetPrimaryFont() const; - void SetFont(const gfx::Font& font); - - // Sets the left and right margin (in pixels) within the text box. On Windows - // this is accomplished by packing the left and right margin into a single - // 32 bit number, so the left and right margins are effectively 16 bits. - void SetHorizontalMargins(int left, int right); - - // Sets the top and bottom margins (in pixels) within the textfield. - // NOTE: in most cases height could be changed instead. - void SetVerticalMargins(int top, int bottom); // Sets the default width of the text control. See default_width_in_chars_. void set_default_width_in_chars(int default_width) { default_width_in_chars_ = default_width; } - // Removes the border from the edit box, giving it a 2D look. - bool draw_border() const { return draw_border_; } - void RemoveBorder(); - // Sets the text to display when empty. - void set_placeholder_text(const string16& text) { + void set_placeholder_text(const base::string16& text) { placeholder_text_ = text; } virtual base::string16 GetPlaceholderText() const; @@ -175,59 +144,40 @@ class VIEWS_EXPORT Textfield : public View { placeholder_text_color_ = color; } - // Getter for the horizontal margins that were set. Returns false if - // horizontal margins weren't set. - bool GetHorizontalMargins(int* left, int* right); - - // Getter for the vertical margins that were set. Returns false if vertical - // margins weren't set. - bool GetVerticalMargins(int* top, int* bottom); + // Get or set the horizontal alignment used for the button from the underlying + // RenderText object. + gfx::HorizontalAlignment GetHorizontalAlignment() const; + void SetHorizontalAlignment(gfx::HorizontalAlignment alignment); - // Updates all properties on the textfield. This is invoked internally. - // Users of Textfield never need to invoke this directly. - void UpdateAllProperties(); - - // Invoked by the edit control when the value changes. This method set - // the text_ member variable to the value contained in edit control. - // This is important because the edit control can be replaced if it has - // been deleted during a window close. - void SyncText(); + // Displays a virtual keyboard or alternate input view if enabled. + void ShowImeIfNeeded(); // Returns whether or not an IME is composing text. bool IsIMEComposing() const; - // Gets the selected range. This is views-implementation only and - // has to be called after the wrapper is created. - // TODO(msw): Return a const reference when NativeTextfieldWin is gone. - gfx::Range GetSelectedRange() const; + // Gets the selected logical text range. + const gfx::Range& GetSelectedRange() const; - // Selects the text given by |range|. This is views-implementation only and - // has to be called after the wrapper is created. + // Selects the specified logical text range. void SelectRange(const gfx::Range& range); - // Gets the selection model. This is views-implementation only and - // has to be called after the wrapper is created. - // TODO(msw): Return a const reference when NativeTextfieldWin is gone. - gfx::SelectionModel GetSelectionModel() const; + // Gets the text selection model. + const gfx::SelectionModel& GetSelectionModel() const; - // Selects the text given by |sel|. This is views-implementation only and - // has to be called after the wrapper is created. + // Sets the specified text selection model. void SelectSelectionModel(const gfx::SelectionModel& sel); - // Returns the current cursor position. This is views-implementation - // only and has to be called after the wrapper is created. + // Returns the current cursor position. size_t GetCursorPosition() const; // Set the text color over the entire text or a logical character range. - // Empty and invalid ranges are ignored. This is views-implementation only and - // has to be called after the wrapper is created. + // Empty and invalid ranges are ignored. void SetColor(SkColor value); void ApplyColor(SkColor value, const gfx::Range& range); // Set various text styles over the entire text or a logical character range. // The respective |style| is applied if |value| is true, or removed if false. - // Empty and invalid ranges are ignored. This is views-implementation only and - // has to be called after the wrapper is created. + // Empty and invalid ranges are ignored. void SetStyle(gfx::TextStyle style, bool value); void ApplyStyle(gfx::TextStyle style, bool value, const gfx::Range& range); @@ -235,69 +185,201 @@ class VIEWS_EXPORT Textfield : public View { void ClearEditHistory(); // Set the accessible name of the text field. - void SetAccessibleName(const string16& name); + void SetAccessibleName(const base::string16& name); // Performs the action associated with the specified command id. void ExecuteCommand(int command_id); void SetFocusPainter(scoped_ptr<Painter> focus_painter); - // Provided only for testing: - gfx::NativeView GetTestingHandle() const { - return native_wrapper_ ? native_wrapper_->GetTestingHandle() : NULL; - } - NativeTextfieldWrapper* GetNativeWrapperForTesting() const { - return native_wrapper_; - } - // Returns whether there is a drag operation originating from the textfield. bool HasTextBeingDragged(); - // Overridden from View: - virtual void Layout() OVERRIDE; + // View overrides: virtual int GetBaseline() const OVERRIDE; - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual const char* GetClassName() const OVERRIDE; + virtual gfx::NativeCursor GetCursor(const ui::MouseEvent& event) 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 bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE; + virtual ui::TextInputClient* GetTextInputClient() OVERRIDE; + virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; virtual void AboutToRequestFocusFromTabTraversal(bool reverse) OVERRIDE; - virtual bool SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) OVERRIDE; + virtual bool SkipDefaultKeyEventProcessing( + const ui::KeyEvent& event) OVERRIDE; + virtual bool GetDropFormats( + int* formats, + std::set<ui::OSExchangeData::CustomFormat>* custom_formats) OVERRIDE; + virtual bool CanDrop(const ui::OSExchangeData& data) OVERRIDE; + virtual int OnDragUpdated(const ui::DropTargetEvent& event) OVERRIDE; + virtual void OnDragExited() OVERRIDE; + virtual int OnPerformDrop(const ui::DropTargetEvent& event) OVERRIDE; + virtual void OnDragDone() OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; + virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE; virtual void OnEnabledChanged() OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; - virtual bool OnKeyPressed(const ui::KeyEvent& e) OVERRIDE; - virtual bool OnKeyReleased(const ui::KeyEvent& e) OVERRIDE; - virtual bool OnMouseDragged(const ui::MouseEvent& e) OVERRIDE; virtual void OnFocus() OVERRIDE; virtual void OnBlur() OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; - virtual ui::TextInputClient* GetTextInputClient() OVERRIDE; virtual gfx::Point GetKeyboardContextMenuLocation() OVERRIDE; + virtual void OnNativeThemeChanged(const ui::NativeTheme* theme) OVERRIDE; + + // TextfieldModel::Delegate overrides: + virtual void OnCompositionTextConfirmedOrCleared() OVERRIDE; + + // ContextMenuController overrides: + virtual void ShowContextMenuForView(View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) OVERRIDE; + + // DragController overrides: + virtual void WriteDragDataForView(View* sender, + const gfx::Point& press_pt, + ui::OSExchangeData* data) OVERRIDE; + virtual int GetDragOperationsForView(View* sender, + const gfx::Point& p) OVERRIDE; + virtual bool CanStartDragForView(View* sender, + const gfx::Point& press_pt, + const gfx::Point& p) OVERRIDE; + + // ui::TouchEditable overrides: + virtual void SelectRect(const gfx::Point& start, + const gfx::Point& end) OVERRIDE; + virtual void MoveCaretTo(const gfx::Point& point) OVERRIDE; + virtual void GetSelectionEndPoints(gfx::Rect* p1, gfx::Rect* p2) OVERRIDE; + virtual gfx::Rect GetBounds() OVERRIDE; + virtual gfx::NativeView GetNativeView() const OVERRIDE; + virtual void ConvertPointToScreen(gfx::Point* point) OVERRIDE; + virtual void ConvertPointFromScreen(gfx::Point* point) OVERRIDE; + virtual bool DrawsHandles() OVERRIDE; + virtual void OpenContextMenu(const gfx::Point& anchor) OVERRIDE; + virtual void DestroyTouchSelection() OVERRIDE; + + // ui::SimpleMenuModel::Delegate overrides: + virtual bool IsCommandIdChecked(int command_id) const OVERRIDE; + virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE; + virtual bool GetAcceleratorForCommandId( + int command_id, + ui::Accelerator* accelerator) OVERRIDE; + virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE; + + // ui::TextInputClient overrides: + virtual void SetCompositionText( + const ui::CompositionText& composition) OVERRIDE; + virtual void ConfirmCompositionText() OVERRIDE; + virtual void ClearCompositionText() OVERRIDE; + virtual void InsertText(const base::string16& text) OVERRIDE; + virtual void InsertChar(base::char16 ch, int flags) OVERRIDE; + virtual gfx::NativeWindow GetAttachedWindow() const OVERRIDE; + virtual ui::TextInputType GetTextInputType() const OVERRIDE; + virtual ui::TextInputMode GetTextInputMode() const OVERRIDE; + virtual bool CanComposeInline() const OVERRIDE; + virtual gfx::Rect GetCaretBounds() const OVERRIDE; + virtual bool GetCompositionCharacterBounds(uint32 index, + gfx::Rect* rect) const OVERRIDE; + virtual bool HasCompositionText() const OVERRIDE; + virtual bool GetTextRange(gfx::Range* range) const OVERRIDE; + virtual bool GetCompositionTextRange(gfx::Range* range) const OVERRIDE; + virtual bool GetSelectionRange(gfx::Range* range) const OVERRIDE; + virtual bool SetSelectionRange(const gfx::Range& range) OVERRIDE; + virtual bool DeleteRange(const gfx::Range& range) OVERRIDE; + virtual bool GetTextFromRange(const gfx::Range& range, + base::string16* text) const OVERRIDE; + virtual void OnInputMethodChanged() OVERRIDE; + virtual bool ChangeTextDirectionAndLayoutAlignment( + base::i18n::TextDirection direction) OVERRIDE; + virtual void ExtendSelectionAndDelete(size_t before, size_t after) OVERRIDE; + virtual void EnsureCaretInRect(const gfx::Rect& rect) OVERRIDE; + virtual void OnCandidateWindowShown() OVERRIDE; + virtual void OnCandidateWindowUpdated() OVERRIDE; + virtual void OnCandidateWindowHidden() OVERRIDE; + virtual bool IsEditingCommandEnabled(int command_id) OVERRIDE; + virtual void ExecuteEditingCommand(int command_id) OVERRIDE; protected: - virtual void ViewHierarchyChanged( - const ViewHierarchyChangedDetails& details) OVERRIDE; - virtual const char* GetClassName() const OVERRIDE; + // Returns the TextfieldModel's text/cursor/selection rendering model. + gfx::RenderText* GetRenderText() const; + + gfx::Point last_click_location() const { return last_click_location_; } - // The object that actually implements the native text field. - NativeTextfieldWrapper* native_wrapper_; + // Get the text from the selection clipboard. + virtual base::string16 GetSelectionClipboardText() const; private: - // Returns the insets to the rectangle where text is actually painted. - gfx::Insets GetTextInsets() const; + friend class TextfieldTestApi; // Handles a request to change the value of this text field from software // using an accessibility API (typically automation software, screen readers // don't normally use this). Sets the value and clears the selection. - void AccessibilitySetValue(const string16& new_value); + void AccessibilitySetValue(const base::string16& new_value); - // This is the current listener for events from this Textfield. - TextfieldController* controller_; + // Updates the painted background color. + void UpdateBackgroundColor(); + + // Does necessary updates when the text and/or cursor position changes. + void UpdateAfterChange(bool text_changed, bool cursor_changed); + + // A callback function to periodically update the cursor state. + void UpdateCursor(); + + // Repaint the cursor. + void RepaintCursor(); + + void PaintTextAndCursor(gfx::Canvas* canvas); + + // Helper function to call MoveCursorTo on the TextfieldModel. + void MoveCursorTo(const gfx::Point& point, bool select); + + // Helper function to update the selection on a mouse drag. + void SelectThroughLastDragLocation(); + + // Convenience method to notify the InputMethod and TouchSelectionController. + void OnCaretBoundsChanged(); + + // Convenience method to call TextfieldController::OnBeforeUserAction(); + void OnBeforeUserAction(); - // The mask of style options for this Textfield. - StyleFlags style_; + // Convenience method to call TextfieldController::OnAfterUserAction(); + void OnAfterUserAction(); - // The fonts used to render the text in the Textfield. - gfx::FontList font_list_; + // Calls |model_->Cut()| and notifies TextfieldController on success. + bool Cut(); - // The text displayed in the Textfield. - string16 text_; + // Calls |model_->Copy()| and notifies TextfieldController on success. + bool Copy(); + + // Calls |model_->Paste()| and calls TextfieldController::ContentsChanged() + // explicitly if paste succeeded. + bool Paste(); + + // Utility function to prepare the context menu. + void UpdateContextMenu(); + + // Tracks the mouse clicks for single/double/triple clicks. + void TrackMouseClicks(const ui::MouseEvent& event); + + // Returns true if the current text input type allows access by the IME. + bool ImeEditingAllowed() const; + + // Reveals the password character at |index| for a set duration. + // If |index| is -1, the existing revealed character will be reset. + void RevealPasswordChar(int index); + + void CreateTouchSelectionControllerAndNotifyIt(); + + // Updates the selection clipboard to any non-empty text selection. + void UpdateSelectionClipboard() const; + + // Pastes the selection clipboard for the specified mouse event. + void PasteSelectionClipboard(const ui::MouseEvent& event); + + // The text model. + scoped_ptr<TextfieldModel> model_; + + // This is the current listener for events from this Textfield. + TextfieldController* controller_; // True if this Textfield cannot accept input and is read-only. bool read_only_; @@ -306,48 +388,72 @@ class VIEWS_EXPORT Textfield : public View { // This will be reported as the "desired size". Defaults to 0. int default_width_in_chars_; - // Whether the border is drawn. - bool draw_border_; - - // Text color. Only used if |use_default_text_color_| is false. - SkColor text_color_; + scoped_ptr<Painter> focus_painter_; - // Should we use the system text color instead of |text_color_|? + // Flags indicating whether various system colors should be used, and if not, + // what overriding color values should be used instead. bool use_default_text_color_; - - // Background color. Only used if |use_default_background_color_| is false. - SkColor background_color_; - - // Should we use the system background color instead of |background_color_|? bool use_default_background_color_; - - // Holds inner textfield margins. - gfx::Insets margins_; - - // Holds whether margins were set. - bool horizontal_margins_were_set_; - bool vertical_margins_were_set_; + bool use_default_selection_text_color_; + bool use_default_selection_background_color_; + SkColor text_color_; + SkColor background_color_; + SkColor selection_text_color_; + SkColor selection_background_color_; // Text to display when empty. - string16 placeholder_text_; + base::string16 placeholder_text_; // Placeholder text color. SkColor placeholder_text_color_; // The accessible name of the text field. - string16 accessible_name_; + base::string16 accessible_name_; // The input type of this text field. ui::TextInputType text_input_type_; - // The duration to reveal the last typed char for obscured textfields. - base::TimeDelta obscured_reveal_duration_; + // The duration and timer to reveal the last typed password character. + base::TimeDelta password_reveal_duration_; + base::OneShotTimer<Textfield> password_reveal_timer_; + + // Tracks whether a user action is being performed; i.e. OnBeforeUserAction() + // has been called, but OnAfterUserAction() has not yet been called. + bool performing_user_action_; + + // True if InputMethod::CancelComposition() should not be called. + bool skip_input_method_cancel_composition_; + + // The text editing cursor repaint timer and visibility. + base::RepeatingTimer<Textfield> cursor_repaint_timer_; + bool cursor_visible_; + + // The drop cursor is a visual cue for where dragged text will be dropped. + bool drop_cursor_visible_; + gfx::SelectionModel drop_cursor_position_; + + // Is the user potentially dragging and dropping from this view? + bool initiating_drag_; + + // A timer and point used to modify the selection when dragging. + base::RepeatingTimer<Textfield> drag_selection_timer_; + gfx::Point last_drag_location_; + + // State variables used to track double and triple clicks. + size_t aggregated_clicks_; + base::TimeDelta last_click_time_; + gfx::Point last_click_location_; + gfx::Range double_click_word_; + + scoped_ptr<ui::TouchSelectionController> touch_selection_controller_; + + // Context menu related members. + scoped_ptr<ui::SimpleMenuModel> context_menu_contents_; + scoped_ptr<views::MenuRunner> context_menu_runner_; // Used to bind callback functions to this object. base::WeakPtrFactory<Textfield> weak_ptr_factory_; - scoped_ptr<Painter> focus_painter_; - DISALLOW_COPY_AND_ASSIGN(Textfield); }; diff --git a/chromium/ui/views/controls/textfield/textfield_controller.cc b/chromium/ui/views/controls/textfield/textfield_controller.cc index 8f72be8a1c4..1d90f3c06df 100644 --- a/chromium/ui/views/controls/textfield/textfield_controller.cc +++ b/chromium/ui/views/controls/textfield/textfield_controller.cc @@ -23,20 +23,4 @@ int TextfieldController::OnDrop(const ui::OSExchangeData& data) { return ui::DragDropTypes::DRAG_NONE; } -bool TextfieldController::IsCommandIdEnabled(int command_id) const { - return false; -} - -bool TextfieldController::IsItemForCommandIdDynamic(int command_id) const { - return false; -} - -string16 TextfieldController::GetLabelForCommandId(int command_id) const { - return string16(); -} - -bool TextfieldController::HandlesCommand(int command_id) const { - return false; -} - } // namespace views diff --git a/chromium/ui/views/controls/textfield/textfield_controller.h b/chromium/ui/views/controls/textfield/textfield_controller.h index d72442b41ad..4a01d384f4b 100644 --- a/chromium/ui/views/controls/textfield/textfield_controller.h +++ b/chromium/ui/views/controls/textfield/textfield_controller.h @@ -29,7 +29,7 @@ class VIEWS_EXPORT TextfieldController { // user. It won't be called if the text is changed by calling // Textfield::SetText() or Textfield::AppendText(). virtual void ContentsChanged(Textfield* sender, - const string16& new_contents) {} + const base::string16& new_contents) {} // This method is called to get notified about keystrokes in the edit. // Returns true if the message was handled and should not be processed @@ -52,7 +52,7 @@ class VIEWS_EXPORT TextfieldController { virtual void OnAfterUserAction(Textfield* sender) {} // Called after performing a Cut or Copy operation. - virtual void OnAfterCutOrCopy() {} + virtual void OnAfterCutOrCopy(ui::ClipboardType clipboard_type) {} // Called after performing a Paste operation. virtual void OnAfterPaste() {} @@ -79,26 +79,6 @@ class VIEWS_EXPORT TextfieldController { // Gives the controller a chance to modify the context menu contents. virtual void UpdateContextMenu(ui::SimpleMenuModel* menu_contents) {} - // Returns true if the |command_id| should be enabled in the context menu. - virtual bool IsCommandIdEnabled(int command_id) const; - - // Returns true if the item label for the |command_id| is dynamic in the - // context menu. - virtual bool IsItemForCommandIdDynamic(int command_id) const; - - // Returns the label string for the |coomand_id|. - virtual string16 GetLabelForCommandId(int command_id) const; - - // Returns whether the controller handles the specified command. This is used - // to handle a command the textfield would normally handle. For example, to - // have the controller handle |IDS_APP_PASTE| override and return true if - // |command_id| == |IDS_APP_PASTE|. - // This is only invoked if the command is enabled. - virtual bool HandlesCommand(int command_id) const; - - // Execute context menu command specified by |command_id|. - virtual void ExecuteCommand(int command_id, int event_flag) {} - protected: virtual ~TextfieldController() {} }; diff --git a/chromium/ui/views/controls/textfield/textfield_views_model.cc b/chromium/ui/views/controls/textfield/textfield_model.cc index 222d0860644..db0dc73b1b0 100644 --- a/chromium/ui/views/controls/textfield/textfield_views_model.cc +++ b/chromium/ui/views/controls/textfield/textfield_model.cc @@ -1,69 +1,57 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2014 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/controls/textfield/textfield_views_model.h" +#include "ui/views/controls/textfield/textfield_model.h" #include <algorithm> -#include "base/i18n/break_iterator.h" #include "base/logging.h" #include "base/stl_util.h" -#include "base/strings/utf_string_conversions.h" #include "ui/base/clipboard/clipboard.h" #include "ui/base/clipboard/scoped_clipboard_writer.h" -#include "ui/gfx/canvas.h" -#include "ui/gfx/font.h" #include "ui/gfx/range/range.h" -#include "ui/gfx/render_text.h" -#include "ui/gfx/text_constants.h" #include "ui/gfx/utf16_indexing.h" -#include "ui/views/controls/textfield/textfield.h" namespace views { namespace internal { -// An edit object holds enough information/state to undo/redo the -// change. Two edits are merged when possible, for example, when -// you type new characters in sequence. |Commit()| can be used to -// mark an edit as an independent edit and it shouldn't be merged. -// (For example, when you did undo/redo, or a text is appended via -// API) +// Edit holds state information to undo/redo editing changes. Editing operations +// are merged when possible, like when characters are typed in sequence. Calling +// Commit() marks an edit as an independent operation that shouldn't be merged. class Edit { public: enum Type { INSERT_EDIT, DELETE_EDIT, - REPLACE_EDIT + REPLACE_EDIT, }; - virtual ~Edit() { - } + virtual ~Edit() {} // Revert the change made by this edit in |model|. - void Undo(TextfieldViewsModel* model) { + void Undo(TextfieldModel* model) { model->ModifyText(new_text_start_, new_text_end(), old_text_, old_text_start_, old_cursor_pos_); } // Apply the change of this edit to the |model|. - void Redo(TextfieldViewsModel* model) { + void Redo(TextfieldModel* model) { model->ModifyText(old_text_start_, old_text_end(), new_text_, new_text_start_, new_cursor_pos_); } - // Try to merge the |edit| into this edit. Returns true if merge was - // successful, or false otherwise. Merged edit will be deleted after - // redo and should not be reused. + // Try to merge the |edit| into this edit and returns true on success. The + // merged edit will be deleted after redo and should not be reused. bool Merge(const Edit* edit) { // Don't merge if previous edit is DELETE. This happens when a // user deletes characters then hits return. In this case, the // delete should be treated as separate edit that can be undone // and should not be merged with the replace edit. - if (type_ != DELETE_EDIT && edit->merge_with_previous()) { + if (type_ != DELETE_EDIT && edit->force_merge()) { MergeReplace(edit); return true; } @@ -81,11 +69,11 @@ class Edit { Edit(Type type, MergeType merge_type, size_t old_cursor_pos, - const string16& old_text, + const base::string16& old_text, size_t old_text_start, bool delete_backward, size_t new_cursor_pos, - const string16& new_text, + const base::string16& new_text, size_t new_text_start) : type_(type), merge_type_(merge_type), @@ -98,8 +86,7 @@ class Edit { new_text_start_(new_text_start) { } - // A template method pattern that provides specific merge - // implementation for each type of edit. + // Each type of edit provides its own specific merge implementation. virtual bool DoMerge(const Edit* edit) = 0; Type type() const { return type_; } @@ -108,9 +95,7 @@ class Edit { bool mergeable() const { return merge_type_ == MERGEABLE; } // Should this edit be forcibly merged with the previous edit? - bool merge_with_previous() const { - return merge_type_ == MERGE_WITH_PREVIOUS; - } + bool force_merge() const { return merge_type_ == FORCE_MERGE; } // Returns the end index of the |old_text_|. size_t old_text_end() const { return old_text_start_ + old_text_.length(); } @@ -118,14 +103,13 @@ class Edit { // Returns the end index of the |new_text_|. size_t new_text_end() const { return new_text_start_ + new_text_.length(); } - // Merge the replace edit into the current edit. This is a special case to - // handle an omnibox setting autocomplete string after new character is - // typed in. + // Merge the replace edit into the current edit. This handles the special case + // where an omnibox autocomplete string is set after a new character is typed. void MergeReplace(const Edit* edit) { CHECK_EQ(REPLACE_EDIT, edit->type_); CHECK_EQ(0U, edit->old_text_start_); CHECK_EQ(0U, edit->new_text_start_); - string16 old_text = edit->old_text_; + base::string16 old_text = edit->old_text_; old_text.erase(new_text_start_, new_text_.length()); old_text.insert(old_text_start_, old_text_); // SetText() replaces entire text. Set |old_text_| to the entire @@ -146,7 +130,7 @@ class Edit { // Old cursor position. size_t old_cursor_pos_; // Deleted text by this edit. - string16 old_text_; + base::string16 old_text_; // The index of |old_text_|. size_t old_text_start_; // True if the deletion is made backward. @@ -154,7 +138,7 @@ class Edit { // New cursor position. size_t new_cursor_pos_; // Added text. - string16 new_text_; + base::string16 new_text_; // The index of |new_text_| size_t new_text_start_; @@ -163,11 +147,11 @@ class Edit { class InsertEdit : public Edit { public: - InsertEdit(bool mergeable, const string16& new_text, size_t at) + InsertEdit(bool mergeable, const base::string16& new_text, size_t at) : Edit(INSERT_EDIT, mergeable ? MERGEABLE : DO_NOT_MERGE, at /* old cursor */, - string16(), + base::string16(), at, false /* N/A */, at + new_text.length() /* new cursor */, @@ -191,12 +175,12 @@ class InsertEdit : public Edit { class ReplaceEdit : public Edit { public: ReplaceEdit(MergeType merge_type, - const string16& old_text, + const base::string16& old_text, size_t old_cursor_pos, size_t old_text_start, bool backward, size_t new_cursor_pos, - const string16& new_text, + const base::string16& new_text, size_t new_text_start) : Edit(REPLACE_EDIT, merge_type, old_cursor_pos, @@ -224,7 +208,7 @@ class ReplaceEdit : public Edit { class DeleteEdit : public Edit { public: DeleteEdit(bool mergeable, - const string16& text, + const base::string16& text, size_t text_start, bool backward) : Edit(DELETE_EDIT, @@ -234,7 +218,7 @@ class DeleteEdit : public Edit { text_start, backward, text_start, - string16(), + base::string16(), text_start) { } @@ -244,16 +228,14 @@ class DeleteEdit : public Edit { return false; if (delete_backward_) { - // backspace can be merged only with backspace at the - // same position. + // backspace can be merged only with backspace at the same position. if (!edit->delete_backward_ || old_text_start_ != edit->old_text_end()) return false; old_text_start_ = edit->old_text_start_; old_text_ = edit->old_text_ + old_text_; new_cursor_pos_ = edit->new_cursor_pos_; } else { - // delete can be merged only with delete at the same - // position. + // delete can be merged only with delete at the same position. if (edit->delete_backward_ || old_text_start_ != edit->old_text_start_) return false; old_text_ += edit->old_text_; @@ -286,70 +268,61 @@ using internal::InsertEdit; using internal::ReplaceEdit; using internal::MergeType; using internal::DO_NOT_MERGE; -using internal::MERGE_WITH_PREVIOUS; +using internal::FORCE_MERGE; using internal::MERGEABLE; ///////////////////////////////////////////////////////////////// -// TextfieldViewsModel: public +// TextfieldModel: public -TextfieldViewsModel::Delegate::~Delegate() { -} +TextfieldModel::Delegate::~Delegate() {} -TextfieldViewsModel::TextfieldViewsModel(Delegate* delegate) +TextfieldModel::TextfieldModel(Delegate* delegate) : delegate_(delegate), render_text_(gfx::RenderText::CreateInstance()), current_edit_(edit_history_.end()) { } -TextfieldViewsModel::~TextfieldViewsModel() { +TextfieldModel::~TextfieldModel() { ClearEditHistory(); ClearComposition(); } -const string16& TextfieldViewsModel::GetText() const { - return render_text_->text(); -} - -bool TextfieldViewsModel::SetText(const string16& text) { +bool TextfieldModel::SetText(const base::string16& new_text) { bool changed = false; if (HasCompositionText()) { ConfirmCompositionText(); changed = true; } - if (GetText() != text) { + if (text() != new_text) { if (changed) // No need to remember composition. Undo(); size_t old_cursor = GetCursorPosition(); // SetText moves the cursor to the end. - size_t new_cursor = text.length(); + size_t new_cursor = new_text.length(); SelectAll(false); // If there is a composition text, don't merge with previous edit. // Otherwise, force merge the edits. - ExecuteAndRecordReplace( - changed ? DO_NOT_MERGE : MERGE_WITH_PREVIOUS, - old_cursor, - new_cursor, - text, - 0U); + ExecuteAndRecordReplace(changed ? DO_NOT_MERGE : FORCE_MERGE, + old_cursor, new_cursor, new_text, 0U); render_text_->SetCursorPosition(new_cursor); } ClearSelection(); return changed; } -void TextfieldViewsModel::Append(const string16& text) { +void TextfieldModel::Append(const base::string16& new_text) { if (HasCompositionText()) ConfirmCompositionText(); size_t save = GetCursorPosition(); MoveCursor(gfx::LINE_BREAK, render_text_->GetVisualDirectionOfLogicalEnd(), false); - InsertText(text); + InsertText(new_text); render_text_->SetCursorPosition(save); ClearSelection(); } -bool TextfieldViewsModel::Delete() { +bool TextfieldModel::Delete() { if (HasCompositionText()) { // No undo/redo for composition text. CancelCompositionText(); @@ -359,7 +332,7 @@ bool TextfieldViewsModel::Delete() { DeleteSelection(); return true; } - if (GetText().length() > GetCursorPosition()) { + if (text().length() > GetCursorPosition()) { size_t cursor_position = GetCursorPosition(); size_t next_grapheme_index = render_text_->IndexOfAdjacentGrapheme( cursor_position, gfx::CURSOR_FORWARD); @@ -370,7 +343,7 @@ bool TextfieldViewsModel::Delete() { return false; } -bool TextfieldViewsModel::Backspace() { +bool TextfieldModel::Backspace() { if (HasCompositionText()) { // No undo/redo for composition text. CancelCompositionText(); @@ -383,103 +356,104 @@ bool TextfieldViewsModel::Backspace() { size_t cursor_position = GetCursorPosition(); if (cursor_position > 0) { // Delete one code point, which may be two UTF-16 words. - size_t previous_char = - gfx::UTF16OffsetToIndex(GetText(), cursor_position, -1); + size_t previous_char = gfx::UTF16OffsetToIndex(text(), cursor_position, -1); ExecuteAndRecordDelete(gfx::Range(cursor_position, previous_char), true); return true; } return false; } -size_t TextfieldViewsModel::GetCursorPosition() const { +size_t TextfieldModel::GetCursorPosition() const { return render_text_->cursor_position(); } -void TextfieldViewsModel::MoveCursor(gfx::BreakType break_type, - gfx::VisualCursorDirection direction, - bool select) { +void TextfieldModel::MoveCursor(gfx::BreakType break_type, + gfx::VisualCursorDirection direction, + bool select) { if (HasCompositionText()) ConfirmCompositionText(); render_text_->MoveCursor(break_type, direction, select); } -bool TextfieldViewsModel::MoveCursorTo(const gfx::SelectionModel& model) { +bool TextfieldModel::MoveCursorTo(const gfx::SelectionModel& cursor) { if (HasCompositionText()) { ConfirmCompositionText(); // ConfirmCompositionText() updates cursor position. Need to reflect it in // the SelectionModel parameter of MoveCursorTo(). - gfx::Range range(render_text_->selection().start(), model.caret_pos()); + gfx::Range range(render_text_->selection().start(), cursor.caret_pos()); if (!range.is_empty()) return render_text_->SelectRange(range); return render_text_->MoveCursorTo( - gfx::SelectionModel(model.caret_pos(), model.caret_affinity())); + gfx::SelectionModel(cursor.caret_pos(), cursor.caret_affinity())); } - return render_text_->MoveCursorTo(model); + return render_text_->MoveCursorTo(cursor); } -bool TextfieldViewsModel::MoveCursorTo(const gfx::Point& point, bool select) { +bool TextfieldModel::MoveCursorTo(const gfx::Point& point, bool select) { if (HasCompositionText()) ConfirmCompositionText(); - return render_text_->MoveCursorTo(point, select); + gfx::SelectionModel cursor = render_text_->FindCursorPosition(point); + if (select) + cursor.set_selection_start(render_text_->selection().start()); + return render_text_->MoveCursorTo(cursor); } -string16 TextfieldViewsModel::GetSelectedText() const { - return GetText().substr(render_text_->selection().GetMin(), - render_text_->selection().length()); +base::string16 TextfieldModel::GetSelectedText() const { + return text().substr(render_text_->selection().GetMin(), + render_text_->selection().length()); } -void TextfieldViewsModel::SelectRange(const gfx::Range& range) { +void TextfieldModel::SelectRange(const gfx::Range& range) { if (HasCompositionText()) ConfirmCompositionText(); render_text_->SelectRange(range); } -void TextfieldViewsModel::SelectSelectionModel(const gfx::SelectionModel& sel) { +void TextfieldModel::SelectSelectionModel(const gfx::SelectionModel& sel) { if (HasCompositionText()) ConfirmCompositionText(); render_text_->MoveCursorTo(sel); } -void TextfieldViewsModel::SelectAll(bool reversed) { +void TextfieldModel::SelectAll(bool reversed) { if (HasCompositionText()) ConfirmCompositionText(); render_text_->SelectAll(reversed); } -void TextfieldViewsModel::SelectWord() { +void TextfieldModel::SelectWord() { if (HasCompositionText()) ConfirmCompositionText(); render_text_->SelectWord(); } -void TextfieldViewsModel::ClearSelection() { +void TextfieldModel::ClearSelection() { if (HasCompositionText()) ConfirmCompositionText(); render_text_->ClearSelection(); } -bool TextfieldViewsModel::CanUndo() { +bool TextfieldModel::CanUndo() { return edit_history_.size() && current_edit_ != edit_history_.end(); } -bool TextfieldViewsModel::CanRedo() { +bool TextfieldModel::CanRedo() { if (!edit_history_.size()) return false; - // There is no redo iff the current edit is the last element - // in the history. + // There is no redo iff the current edit is the last element in the history. EditHistory::iterator iter = current_edit_; return iter == edit_history_.end() || // at the top. ++iter != edit_history_.end(); } -bool TextfieldViewsModel::Undo() { +bool TextfieldModel::Undo() { if (!CanUndo()) return false; DCHECK(!HasCompositionText()); - if (HasCompositionText()) // safe guard for release build. + if (HasCompositionText()) CancelCompositionText(); - string16 old = GetText(); + base::string16 old = text(); size_t old_cursor = GetCursorPosition(); (*current_edit_)->Commit(); (*current_edit_)->Undo(this); @@ -488,27 +462,27 @@ bool TextfieldViewsModel::Undo() { current_edit_ = edit_history_.end(); else current_edit_--; - return old != GetText() || old_cursor != GetCursorPosition(); + return old != text() || old_cursor != GetCursorPosition(); } -bool TextfieldViewsModel::Redo() { +bool TextfieldModel::Redo() { if (!CanRedo()) return false; DCHECK(!HasCompositionText()); - if (HasCompositionText()) // safe guard for release build. + if (HasCompositionText()) CancelCompositionText(); if (current_edit_ == edit_history_.end()) current_edit_ = edit_history_.begin(); else current_edit_ ++; - string16 old = GetText(); + base::string16 old = text(); size_t old_cursor = GetCursorPosition(); (*current_edit_)->Redo(this); - return old != GetText() || old_cursor != GetCursorPosition(); + return old != text() || old_cursor != GetCursorPosition(); } -bool TextfieldViewsModel::Cut() { +bool TextfieldModel::Cut() { if (!HasCompositionText() && HasSelection() && !render_text_->obscured()) { ui::ScopedClipboardWriter( ui::Clipboard::GetForCurrentThread(), @@ -526,7 +500,7 @@ bool TextfieldViewsModel::Cut() { return false; } -bool TextfieldViewsModel::Copy() { +bool TextfieldModel::Copy() { if (!HasCompositionText() && HasSelection() && !render_text_->obscured()) { ui::ScopedClipboardWriter( ui::Clipboard::GetForCurrentThread(), @@ -536,49 +510,50 @@ bool TextfieldViewsModel::Copy() { return false; } -bool TextfieldViewsModel::Paste() { - string16 result; +bool TextfieldModel::Paste() { + base::string16 result; ui::Clipboard::GetForCurrentThread()->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &result); - if (!result.empty()) { - InsertTextInternal(result, false); - return true; - } - return false; + if (result.empty()) + return false; + + InsertTextInternal(result, false); + return true; } -bool TextfieldViewsModel::HasSelection() const { +bool TextfieldModel::HasSelection() const { return !render_text_->selection().is_empty(); } -void TextfieldViewsModel::DeleteSelection() { +void TextfieldModel::DeleteSelection() { DCHECK(!HasCompositionText()); DCHECK(HasSelection()); ExecuteAndRecordDelete(render_text_->selection(), false); } -void TextfieldViewsModel::DeleteSelectionAndInsertTextAt( - const string16& text, size_t position) { +void TextfieldModel::DeleteSelectionAndInsertTextAt( + const base::string16& new_text, + size_t position) { if (HasCompositionText()) CancelCompositionText(); ExecuteAndRecordReplace(DO_NOT_MERGE, GetCursorPosition(), - position + text.length(), - text, + position + new_text.length(), + new_text, position); } -string16 TextfieldViewsModel::GetTextFromRange(const gfx::Range& range) const { - if (range.IsValid() && range.GetMin() < GetText().length()) - return GetText().substr(range.GetMin(), range.length()); - return string16(); +base::string16 TextfieldModel::GetTextFromRange(const gfx::Range& range) const { + if (range.IsValid() && range.GetMin() < text().length()) + return text().substr(range.GetMin(), range.length()); + return base::string16(); } -void TextfieldViewsModel::GetTextRange(gfx::Range* range) const { - *range = gfx::Range(0, GetText().length()); +void TextfieldModel::GetTextRange(gfx::Range* range) const { + *range = gfx::Range(0, text().length()); } -void TextfieldViewsModel::SetCompositionText( +void TextfieldModel::SetCompositionText( const ui::CompositionText& composition) { if (HasCompositionText()) CancelCompositionText(); @@ -589,7 +564,7 @@ void TextfieldViewsModel::SetCompositionText( return; size_t cursor = GetCursorPosition(); - string16 new_text = GetText(); + base::string16 new_text = text(); render_text_->SetText(new_text.insert(cursor, composition.text)); gfx::Range range(cursor, cursor + composition.text.length()); render_text_->SetCompositionRange(range); @@ -601,8 +576,7 @@ void TextfieldViewsModel::SetCompositionText( // Because the target clause is more important than the actual selection // range (or caret position) in the composition here we use a selection-like // marker instead to show this range. - // TODO(yukawa, msw): Support thick underline in RenderText and remove - // this workaround. + // TODO(yukawa, msw): Support thick underlines and remove this workaround. render_text_->SelectRange(gfx::Range( cursor + emphasized_range.GetMin(), cursor + emphasized_range.GetMax())); @@ -615,67 +589,72 @@ void TextfieldViewsModel::SetCompositionText( } } -void TextfieldViewsModel::ConfirmCompositionText() { +void TextfieldModel::ConfirmCompositionText() { DCHECK(HasCompositionText()); gfx::Range range = render_text_->GetCompositionRange(); - string16 text = GetText().substr(range.start(), range.length()); + base::string16 composition = text().substr(range.start(), range.length()); // TODO(oshima): current behavior on ChromeOS is a bit weird and not // sure exactly how this should work. Find out and fix if necessary. - AddOrMergeEditHistory(new InsertEdit(false, text, range.start())); + AddOrMergeEditHistory(new InsertEdit(false, composition, range.start())); render_text_->SetCursorPosition(range.end()); ClearComposition(); if (delegate_) delegate_->OnCompositionTextConfirmedOrCleared(); } -void TextfieldViewsModel::CancelCompositionText() { +void TextfieldModel::CancelCompositionText() { DCHECK(HasCompositionText()); gfx::Range range = render_text_->GetCompositionRange(); ClearComposition(); - string16 new_text = GetText(); + base::string16 new_text = text(); render_text_->SetText(new_text.erase(range.start(), range.length())); render_text_->SetCursorPosition(range.start()); if (delegate_) delegate_->OnCompositionTextConfirmedOrCleared(); } -void TextfieldViewsModel::ClearComposition() { +void TextfieldModel::ClearComposition() { render_text_->SetCompositionRange(gfx::Range::InvalidRange()); } -void TextfieldViewsModel::GetCompositionTextRange(gfx::Range* range) const { +void TextfieldModel::GetCompositionTextRange(gfx::Range* range) const { *range = gfx::Range(render_text_->GetCompositionRange()); } -bool TextfieldViewsModel::HasCompositionText() const { +bool TextfieldModel::HasCompositionText() const { return !render_text_->GetCompositionRange().is_empty(); } +void TextfieldModel::ClearEditHistory() { + STLDeleteElements(&edit_history_); + current_edit_ = edit_history_.end(); +} + ///////////////////////////////////////////////////////////////// -// TextfieldViewsModel: private +// TextfieldModel: private -void TextfieldViewsModel::InsertTextInternal(const string16& text, - bool mergeable) { +void TextfieldModel::InsertTextInternal(const base::string16& new_text, + bool mergeable) { if (HasCompositionText()) { CancelCompositionText(); - ExecuteAndRecordInsert(text, mergeable); + ExecuteAndRecordInsert(new_text, mergeable); } else if (HasSelection()) { ExecuteAndRecordReplaceSelection(mergeable ? MERGEABLE : DO_NOT_MERGE, - text); + new_text); } else { - ExecuteAndRecordInsert(text, mergeable); + ExecuteAndRecordInsert(new_text, mergeable); } } -void TextfieldViewsModel::ReplaceTextInternal(const string16& text, - bool mergeable) { +void TextfieldModel::ReplaceTextInternal(const base::string16& new_text, + bool mergeable) { if (HasCompositionText()) { CancelCompositionText(); } else if (!HasSelection()) { size_t cursor = GetCursorPosition(); const gfx::SelectionModel& model = render_text_->selection_model(); // When there is no selection, the default is to replace the next grapheme - // with |text|. So, need to find the index of next grapheme first. + // with |new_text|. So, need to find the index of next grapheme first. size_t next = render_text_->IndexOfAdjacentGrapheme(cursor, gfx::CURSOR_FORWARD); if (next == model.caret_pos()) @@ -684,15 +663,10 @@ void TextfieldViewsModel::ReplaceTextInternal(const string16& text, render_text_->SelectRange(gfx::Range(next, model.caret_pos())); } // Edit history is recorded in InsertText. - InsertTextInternal(text, mergeable); -} - -void TextfieldViewsModel::ClearEditHistory() { - STLDeleteElements(&edit_history_); - current_edit_ = edit_history_.end(); + InsertTextInternal(new_text, mergeable); } -void TextfieldViewsModel::ClearRedoHistory() { +void TextfieldModel::ClearRedoHistory() { if (edit_history_.begin() == edit_history_.end()) return; if (current_edit_ == edit_history_.end()) { @@ -705,20 +679,20 @@ void TextfieldViewsModel::ClearRedoHistory() { edit_history_.erase(delete_start, edit_history_.end()); } -void TextfieldViewsModel::ExecuteAndRecordDelete(gfx::Range range, - bool mergeable) { +void TextfieldModel::ExecuteAndRecordDelete(gfx::Range range, bool mergeable) { size_t old_text_start = range.GetMin(); - const string16 text = GetText().substr(old_text_start, range.length()); + const base::string16 old_text = text().substr(old_text_start, range.length()); bool backward = range.is_reversed(); - Edit* edit = new DeleteEdit(mergeable, text, old_text_start, backward); + Edit* edit = new DeleteEdit(mergeable, old_text, old_text_start, backward); bool delete_edit = AddOrMergeEditHistory(edit); edit->Redo(this); if (delete_edit) delete edit; } -void TextfieldViewsModel::ExecuteAndRecordReplaceSelection( - MergeType merge_type, const string16& new_text) { +void TextfieldModel::ExecuteAndRecordReplaceSelection( + MergeType merge_type, + const base::string16& new_text) { size_t new_text_start = render_text_->selection().GetMin(); size_t new_cursor_pos = new_text_start + new_text.length(); ExecuteAndRecordReplace(merge_type, @@ -728,11 +702,11 @@ void TextfieldViewsModel::ExecuteAndRecordReplaceSelection( new_text_start); } -void TextfieldViewsModel::ExecuteAndRecordReplace(MergeType merge_type, - size_t old_cursor_pos, - size_t new_cursor_pos, - const string16& new_text, - size_t new_text_start) { +void TextfieldModel::ExecuteAndRecordReplace(MergeType merge_type, + size_t old_cursor_pos, + size_t new_cursor_pos, + const base::string16& new_text, + size_t new_text_start) { size_t old_text_start = render_text_->selection().GetMin(); bool backward = render_text_->selection().is_reversed(); Edit* edit = new ReplaceEdit(merge_type, @@ -749,28 +723,27 @@ void TextfieldViewsModel::ExecuteAndRecordReplace(MergeType merge_type, delete edit; } -void TextfieldViewsModel::ExecuteAndRecordInsert(const string16& text, - bool mergeable) { - Edit* edit = new InsertEdit(mergeable, text, GetCursorPosition()); +void TextfieldModel::ExecuteAndRecordInsert(const base::string16& new_text, + bool mergeable) { + Edit* edit = new InsertEdit(mergeable, new_text, GetCursorPosition()); bool delete_edit = AddOrMergeEditHistory(edit); edit->Redo(this); if (delete_edit) delete edit; } -bool TextfieldViewsModel::AddOrMergeEditHistory(Edit* edit) { +bool TextfieldModel::AddOrMergeEditHistory(Edit* edit) { ClearRedoHistory(); if (current_edit_ != edit_history_.end() && (*current_edit_)->Merge(edit)) { - // If a current edit exists and has been merged with a new edit, - // don't add to the history, and return true to delete |edit| after - // redo. + // If a current edit exists and has been merged with a new edit, don't add + // to the history, and return true to delete |edit| after redo. return true; } edit_history_.push_back(edit); if (current_edit_ == edit_history_.end()) { - // If there is no redoable edit, this is the 1st edit because - // RedoHistory has been already deleted. + // If there is no redoable edit, this is the 1st edit because RedoHistory + // has been already deleted. DCHECK_EQ(1u, edit_history_.size()); current_edit_ = edit_history_.begin(); } else { @@ -779,21 +752,20 @@ bool TextfieldViewsModel::AddOrMergeEditHistory(Edit* edit) { return false; } -void TextfieldViewsModel::ModifyText(size_t delete_from, - size_t delete_to, - const string16& new_text, - size_t new_text_insert_at, - size_t new_cursor_pos) { +void TextfieldModel::ModifyText(size_t delete_from, + size_t delete_to, + const base::string16& new_text, + size_t new_text_insert_at, + size_t new_cursor_pos) { DCHECK_LE(delete_from, delete_to); - string16 text = GetText(); + base::string16 old_text = text(); ClearComposition(); if (delete_from != delete_to) - render_text_->SetText(text.erase(delete_from, delete_to - delete_from)); + render_text_->SetText(old_text.erase(delete_from, delete_to - delete_from)); if (!new_text.empty()) - render_text_->SetText(text.insert(new_text_insert_at, new_text)); + render_text_->SetText(old_text.insert(new_text_insert_at, new_text)); render_text_->SetCursorPosition(new_cursor_pos); - // TODO(oshima): mac selects the text that is just undone (but gtk doesn't). - // This looks fine feature and we may want to do the same. + // TODO(oshima): Select text that was just undone, like Mac (but not GTK). } } // namespace views diff --git a/chromium/ui/views/controls/textfield/textfield_views_model.h b/chromium/ui/views/controls/textfield/textfield_model.h index 1af734054a9..6ae3398ee46 100644 --- a/chromium/ui/views/controls/textfield/textfield_views_model.h +++ b/chromium/ui/views/controls/textfield/textfield_model.h @@ -1,9 +1,9 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2014 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_CONTROLS_TEXTFIELD_TEXTFIELD_VIEWS_MODEL_H_ -#define UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_VIEWS_MODEL_H_ +#ifndef UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_MODEL_H_ +#define UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_MODEL_H_ #include <list> #include <vector> @@ -11,44 +11,34 @@ #include "base/gtest_prod_util.h" #include "base/memory/scoped_ptr.h" #include "base/strings/string16.h" -#include "third_party/skia/include/core/SkColor.h" #include "ui/base/ime/composition_text.h" -#include "ui/gfx/rect.h" #include "ui/gfx/render_text.h" #include "ui/gfx/text_constants.h" #include "ui/views/views_export.h" -namespace gfx { -class Range; -class RenderText; -} // namespace gfx - namespace views { namespace internal { // Internal Edit class that keeps track of edits for undo/redo. class Edit; -// C++ doesn't allow forward decl enum, so let's define here. +// The types of merge behavior implemented by Edit operations. enum MergeType { - // The edit should not be merged with next edit. It still may - // be merged with an edit with MERGE_WITH_PREVIOUS. + // The edit should not usually be merged with next edit. DO_NOT_MERGE, - // The edit can be merged with next edit when possible. + // The edit should be merged with next edit when possible. MERGEABLE, - // Does the edit have to be merged with previous edit? - // This forces the merge even if the previous edit is marked - // as DO_NOT_MERGE. - MERGE_WITH_PREVIOUS, + // The edit should be merged with the prior edit, even if marked DO_NOT_MERGE. + FORCE_MERGE, }; } // namespace internal -// A model that represents a text content for TextfieldViews. +// A model that represents text content for a views::Textfield. // It supports editing, selection and cursor manipulation. -class VIEWS_EXPORT TextfieldViewsModel { +class VIEWS_EXPORT TextfieldModel { public: - // Delegate interface implemented by the textfield view class to provided + // Delegate interface implemented by the textfield view class to provide // additional functionalities required by the model. class VIEWS_EXPORT Delegate { public: @@ -59,47 +49,46 @@ class VIEWS_EXPORT TextfieldViewsModel { virtual ~Delegate(); }; - explicit TextfieldViewsModel(Delegate* delegate); - virtual ~TextfieldViewsModel(); + explicit TextfieldModel(Delegate* delegate); + virtual ~TextfieldModel(); // Edit related methods. - const string16& GetText() const; - // Sets the text. Returns true if the text has been modified. The - // current composition text will be confirmed first. Setting - // the same text will not add edit history because it's not user - // visible change nor user-initiated change. This allow a client - // code to set the same text multiple times without worrying about - // messing edit history. - bool SetText(const string16& text); + const base::string16& text() const { return render_text_->text(); } + // Sets the text. Returns true if the text has been modified. The current + // composition text will be confirmed first. Setting the same text will not + // add edit history because it's not user visible change nor user-initiated + // change. This allow a client code to set the same text multiple times + // without worrying about messing edit history. + bool SetText(const base::string16& new_text); gfx::RenderText* render_text() { return render_text_.get(); } - // Inserts given |text| at the current cursor position. + // Inserts given |new_text| at the current cursor position. // The current composition text will be cleared. - void InsertText(const string16& text) { - InsertTextInternal(text, false); + void InsertText(const base::string16& new_text) { + InsertTextInternal(new_text, false); } // Inserts a character at the current cursor position. - void InsertChar(char16 c) { - InsertTextInternal(string16(&c, 1), true); + void InsertChar(base::char16 c) { + InsertTextInternal(base::string16(&c, 1), true); } // Replaces characters at the current position with characters in given text. // The current composition text will be cleared. - void ReplaceText(const string16& text) { - ReplaceTextInternal(text, false); + void ReplaceText(const base::string16& new_text) { + ReplaceTextInternal(new_text, false); } // Replaces the char at the current position with given character. - void ReplaceChar(char16 c) { - ReplaceTextInternal(string16(&c, 1), true); + void ReplaceChar(base::char16 c) { + ReplaceTextInternal(base::string16(&c, 1), true); } // Appends the text. // The current composition text will be confirmed. - void Append(const string16& text); + void Append(const base::string16& new_text); // Deletes the first character after the current cursor position (as if, the // the user has pressed delete key in the textfield). Returns true if @@ -124,19 +113,17 @@ class VIEWS_EXPORT TextfieldViewsModel { gfx::VisualCursorDirection direction, bool select); - // Moves the selection to the specified selection in |selection|. - // If there is composition text, it will be confirmed, which will update the - // selection range, and it overrides the selection_start to which the - // selection will move to. - bool MoveCursorTo(const gfx::SelectionModel& selection); + // Updates the cursor to the specified selection model. Any composition text + // will be confirmed, which may alter the specified selection range start. + bool MoveCursorTo(const gfx::SelectionModel& cursor); - // Helper function to call MoveCursorTo on the TextfieldViewsModel. + // Helper function to call MoveCursorTo on the TextfieldModel. bool MoveCursorTo(const gfx::Point& point, bool select); - // Selection related method + // Selection related methods. // Returns the selected text. - string16 GetSelectedText() const; + base::string16 GetSelectedText() const; // The current composition text will be confirmed. The selection starts with // the range's start position, and ends with the range's end position, @@ -195,13 +182,12 @@ class VIEWS_EXPORT TextfieldViewsModel { // composition text. void DeleteSelection(); - // Deletes the selected text (if any) and insert text at given - // position. - void DeleteSelectionAndInsertTextAt( - const string16& text, size_t position); + // Deletes the selected text (if any) and insert text at given position. + void DeleteSelectionAndInsertTextAt(const base::string16& new_text, + size_t position); // Retrieves the text content in a given range. - string16 GetTextFromRange(const gfx::Range& range) const; + base::string16 GetTextFromRange(const gfx::Range& range) const; // Retrieves the range containing all text in the model. void GetTextRange(gfx::Range* range) const; @@ -226,30 +212,23 @@ class VIEWS_EXPORT TextfieldViewsModel { // Returns true if there is composition text. bool HasCompositionText() const; + // Clears all edit history. + void ClearEditHistory(); + private: - friend class NativeTextfieldViews; - friend class NativeTextfieldViewsTest; - friend class TextfieldViewsModelTest; - friend class UndoRedo_BasicTest; - friend class UndoRedo_CutCopyPasteTest; - friend class UndoRedo_ReplaceTest; friend class internal::Edit; - FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, UndoRedo_BasicTest); - FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, UndoRedo_CutCopyPasteTest); - FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, UndoRedo_ReplaceTest); + FRIEND_TEST_ALL_PREFIXES(TextfieldModelTest, UndoRedo_BasicTest); + FRIEND_TEST_ALL_PREFIXES(TextfieldModelTest, UndoRedo_CutCopyPasteTest); + FRIEND_TEST_ALL_PREFIXES(TextfieldModelTest, UndoRedo_ReplaceTest); - // Insert the given |text|. |mergeable| indicates if this insert - // operation can be merged to previous edit in the edit history. - void InsertTextInternal(const string16& text, bool mergeable); + // Insert the given |new_text|. |mergeable| indicates if this insert operation + // can be merged with previous edits in the history. + void InsertTextInternal(const base::string16& new_text, bool mergeable); - // Replace the current text with the given |text|. |mergeable| - // indicates if this replace operation can be merged to previous - // edit in the edit history. - void ReplaceTextInternal(const string16& text, bool mergeable); - - // Clears all edit history. - void ClearEditHistory(); + // Replace the current text with the given |new_text|. |mergeable| indicates + // if this replace operation can be merged with previous edits in the history. + void ReplaceTextInternal(const base::string16& new_text, bool mergeable); // Clears redo history. void ClearRedoHistory(); @@ -257,13 +236,13 @@ class VIEWS_EXPORT TextfieldViewsModel { // Executes and records edit operations. void ExecuteAndRecordDelete(gfx::Range range, bool mergeable); void ExecuteAndRecordReplaceSelection(internal::MergeType merge_type, - const string16& text); + const base::string16& new_text); void ExecuteAndRecordReplace(internal::MergeType merge_type, size_t old_cursor_pos, size_t new_cursor_pos, - const string16& text, + const base::string16& new_text, size_t new_text_start); - void ExecuteAndRecordInsert(const string16& text, bool mergeable); + void ExecuteAndRecordInsert(const base::string16& new_text, bool mergeable); // Adds or merge |edit| into edit history. Return true if the edit // has been merged and must be deleted after redo. @@ -276,14 +255,13 @@ class VIEWS_EXPORT TextfieldViewsModel { // 3) Move the cursor to |new_cursor_pos|. void ModifyText(size_t delete_from, size_t delete_to, - const string16& new_text, + const base::string16& new_text, size_t new_text_insert_at, size_t new_cursor_pos); void ClearComposition(); - // Pointer to a TextfieldViewsModel::Delegate instance, should be provided by - // the View object. + // The TextfieldModel::Delegate instance should be provided by the owner. Delegate* delegate_; // The stylized text, cursor, selection, and the visual layout model. @@ -295,19 +273,20 @@ class VIEWS_EXPORT TextfieldViewsModel { // An iterator that points to the current edit that can be undone. // This iterator moves from the |end()|, meaining no edit to undo, // to the last element (one before |end()|), meaning no edit to redo. - // There is no edit to undo (== end()) when: - // 1) in initial state. (nothing to undo) - // 2) very 1st edit is undone. - // 3) all edit history is removed. - // There is no edit to redo (== last element or no element) when: - // 1) in initial state. (nothing to redo) - // 2) new edit is added. (redo history is cleared) - // 3) redone all undone edits. + // + // There is no edit to undo (== end()) when: + // 1) in initial state. (nothing to undo) + // 2) very 1st edit is undone. + // 3) all edit history is removed. + // There is no edit to redo (== last element or no element) when: + // 1) in initial state. (nothing to redo) + // 2) new edit is added. (redo history is cleared) + // 3) redone all undone edits. EditHistory::iterator current_edit_; - DISALLOW_COPY_AND_ASSIGN(TextfieldViewsModel); + DISALLOW_COPY_AND_ASSIGN(TextfieldModel); }; } // namespace views -#endif // UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_VIEWS_MODEL_H_ +#endif // UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_MODEL_H_ diff --git a/chromium/ui/views/controls/textfield/textfield_views_model_unittest.cc b/chromium/ui/views/controls/textfield/textfield_model_unittest.cc index b23fb10b453..f3947ff0c1c 100644 --- a/chromium/ui/views/controls/textfield/textfield_views_model_unittest.cc +++ b/chromium/ui/views/controls/textfield/textfield_model_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2014 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. @@ -15,7 +15,7 @@ #include "ui/gfx/range/range.h" #include "ui/gfx/render_text.h" #include "ui/views/controls/textfield/textfield.h" -#include "ui/views/controls/textfield/textfield_views_model.h" +#include "ui/views/controls/textfield/textfield_model.h" #include "ui/views/test/test_views_delegate.h" #include "ui/views/test/views_test_base.h" @@ -23,7 +23,7 @@ #include "base/win/windows_version.h" #endif -#define EXPECT_STR_EQ(ascii, utf16) EXPECT_EQ(ASCIIToUTF16(ascii), utf16) +#define EXPECT_STR_EQ(ascii, utf16) EXPECT_EQ(base::ASCIIToUTF16(ascii), utf16) namespace { @@ -34,7 +34,7 @@ struct WordAndCursor { size_t cursor; }; -void MoveCursorTo(views::TextfieldViewsModel& model, size_t pos) { +void MoveCursorTo(views::TextfieldModel& model, size_t pos) { model.MoveCursorTo(gfx::SelectionModel(pos, gfx::CURSOR_FORWARD)); } @@ -42,10 +42,10 @@ void MoveCursorTo(views::TextfieldViewsModel& model, size_t pos) { namespace views { -class TextfieldViewsModelTest : public ViewsTestBase, - public TextfieldViewsModel::Delegate { +class TextfieldModelTest : public ViewsTestBase, + public TextfieldModel::Delegate { public: - TextfieldViewsModelTest() + TextfieldModelTest() : ViewsTestBase(), composition_text_confirmed_or_cleared_(false) { } @@ -55,123 +55,116 @@ class TextfieldViewsModelTest : public ViewsTestBase, } protected: - void ResetModel(TextfieldViewsModel* model) const { - model->SetText(string16()); + void ResetModel(TextfieldModel* model) const { + model->SetText(base::string16()); model->ClearEditHistory(); } bool composition_text_confirmed_or_cleared_; private: - DISALLOW_COPY_AND_ASSIGN(TextfieldViewsModelTest); + DISALLOW_COPY_AND_ASSIGN(TextfieldModelTest); }; -TEST_F(TextfieldViewsModelTest, EditString) { - TextfieldViewsModel model(NULL); - // append two strings - model.Append(ASCIIToUTF16("HILL")); - EXPECT_STR_EQ("HILL", model.GetText()); - model.Append(ASCIIToUTF16("WORLD")); - EXPECT_STR_EQ("HILLWORLD", model.GetText()); +TEST_F(TextfieldModelTest, EditString) { + TextfieldModel model(NULL); + // Append two strings. + model.Append(base::ASCIIToUTF16("HILL")); + EXPECT_STR_EQ("HILL", model.text()); + model.Append(base::ASCIIToUTF16("WORLD")); + EXPECT_STR_EQ("HILLWORLD", model.text()); - // Insert "E" to make hello + // Insert "E" and replace "I" with "L" to make "HELLO". model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); model.InsertChar('E'); - EXPECT_STR_EQ("HEILLWORLD", model.GetText()); - // Replace "I" with "L" + EXPECT_STR_EQ("HEILLWORLD", model.text()); model.ReplaceChar('L'); - EXPECT_STR_EQ("HELLLWORLD", model.GetText()); + EXPECT_STR_EQ("HELLLWORLD", model.text()); model.ReplaceChar('L'); model.ReplaceChar('O'); - EXPECT_STR_EQ("HELLOWORLD", model.GetText()); + EXPECT_STR_EQ("HELLOWORLD", model.text()); - // Delete 6th char "W", then delete 5th char O" + // Delete 6th char "W", then delete 5th char "O". EXPECT_EQ(5U, model.GetCursorPosition()); EXPECT_TRUE(model.Delete()); - EXPECT_STR_EQ("HELLOORLD", model.GetText()); + EXPECT_STR_EQ("HELLOORLD", model.text()); EXPECT_TRUE(model.Backspace()); EXPECT_EQ(4U, model.GetCursorPosition()); - EXPECT_STR_EQ("HELLORLD", model.GetText()); + EXPECT_STR_EQ("HELLORLD", model.text()); - // Move the cursor to start. backspace should fail. + // Move the cursor to start; backspace should fail. model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false); EXPECT_FALSE(model.Backspace()); - EXPECT_STR_EQ("HELLORLD", model.GetText()); - // Move the cursor to the end. delete should fail. + EXPECT_STR_EQ("HELLORLD", model.text()); + // Move the cursor to the end; delete should fail, but backspace should work. model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); EXPECT_FALSE(model.Delete()); - EXPECT_STR_EQ("HELLORLD", model.GetText()); - // but backspace should work. + EXPECT_STR_EQ("HELLORLD", model.text()); EXPECT_TRUE(model.Backspace()); - EXPECT_STR_EQ("HELLORL", model.GetText()); + EXPECT_STR_EQ("HELLORL", model.text()); MoveCursorTo(model, 5); - model.ReplaceText(ASCIIToUTF16(" WOR")); - EXPECT_STR_EQ("HELLO WORL", model.GetText()); + model.ReplaceText(base::ASCIIToUTF16(" WOR")); + EXPECT_STR_EQ("HELLO WORL", model.text()); } -TEST_F(TextfieldViewsModelTest, EditString_SimpleRTL) { - TextfieldViewsModel model(NULL); +TEST_F(TextfieldModelTest, EditString_SimpleRTL) { + TextfieldModel model(NULL); // Append two strings. - model.Append(WideToUTF16(L"\x05d0\x05d1\x05d2")); - EXPECT_EQ(WideToUTF16(L"\x05d0\x05d1\x05d2"), model.GetText()); - model.Append(WideToUTF16(L"\x05e0\x05e1\x05e2")); - EXPECT_EQ(WideToUTF16(L"\x05d0\x05d1\x05d2\x05e0\x05e1\x05e2"), - model.GetText()); + model.Append(base::WideToUTF16(L"\x05d0\x05d1\x05d2")); + EXPECT_EQ(base::WideToUTF16(L"\x05d0\x05d1\x05d2"), model.text()); + model.Append(base::WideToUTF16(L"\x05e0\x05e1\x05e2")); + EXPECT_EQ(base::WideToUTF16(L"\x05d0\x05d1\x05d2\x05e0\x05e1\x05e2"), + model.text()); - // Insert 0x05f0. + // Insert "\x05f0". MoveCursorTo(model, 1); model.InsertChar(0x05f0); - EXPECT_EQ(WideToUTF16(L"\x05d0\x05f0\x05d1\x05d2\x05e0\x05e1\x05e2"), - model.GetText()); + EXPECT_EQ(base::WideToUTF16(L"\x05d0\x05f0\x05d1\x05d2\x05e0\x05e1\x05e2"), + model.text()); // Replace "\x05d1" with "\x05f1". model.ReplaceChar(0x05f1); - EXPECT_EQ(WideToUTF16(L"\x05d0\x05f0\x5f1\x05d2\x05e0\x05e1\x05e2"), - model.GetText()); + EXPECT_EQ(base::WideToUTF16(L"\x05d0\x05f0\x5f1\x05d2\x05e0\x05e1\x05e2"), + model.text()); - // Delete and backspace. + // Test Delete and backspace. EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_TRUE(model.Delete()); - EXPECT_EQ(WideToUTF16(L"\x05d0\x05f0\x5f1\x05e0\x05e1\x05e2"), - model.GetText()); + EXPECT_EQ(base::WideToUTF16(L"\x05d0\x05f0\x5f1\x05e0\x05e1\x05e2"), + model.text()); EXPECT_TRUE(model.Backspace()); EXPECT_EQ(2U, model.GetCursorPosition()); - EXPECT_EQ(WideToUTF16(L"\x05d0\x05f0\x05e0\x05e1\x05e2"), model.GetText()); + EXPECT_EQ(base::WideToUTF16(L"\x05d0\x05f0\x05e0\x05e1\x05e2"), model.text()); } -TEST_F(TextfieldViewsModelTest, EditString_ComplexScript) { - // TODO(asvitkine): Disable tests that fail on XP bots due to lack of complete - // font support for some scripts - http://crbug.com/106450 +TEST_F(TextfieldModelTest, EditString_ComplexScript) { + // TODO(msw): XP fails due to lack of font support: http://crbug.com/106450 bool on_windows_xp = false; #if defined(OS_WIN) on_windows_xp = base::win::GetVersion() < base::win::VERSION_VISTA; #endif - TextfieldViewsModel model(NULL); + TextfieldModel model(NULL); // Append two Hindi strings. - model.Append(WideToUTF16(L"\x0915\x093f\x0915\x094d\x0915")); - EXPECT_EQ(WideToUTF16(L"\x0915\x093f\x0915\x094d\x0915"), - model.GetText()); - model.Append(WideToUTF16(L"\x0915\x094d\x092e\x094d")); - EXPECT_EQ(WideToUTF16( - L"\x0915\x093f\x0915\x094d\x0915\x0915\x094d\x092e\x094d"), - model.GetText()); - - // TODO(asvitkine): Disable tests that fail on XP bots due to lack of complete - // font support for some scripts - http://crbug.com/106450 + model.Append(base::WideToUTF16(L"\x0915\x093f\x0915\x094d\x0915")); + EXPECT_EQ(base::WideToUTF16(L"\x0915\x093f\x0915\x094d\x0915"), model.text()); + model.Append(base::WideToUTF16(L"\x0915\x094d\x092e\x094d")); + EXPECT_EQ(base::WideToUTF16( + L"\x0915\x093f\x0915\x094d\x0915\x0915\x094d\x092e\x094d"), model.text()); + if (!on_windows_xp) { - // Check it is not able to place cursor in middle of a grapheme. + // Ensure the cursor cannot be placed in the middle of a grapheme. MoveCursorTo(model, 1); EXPECT_EQ(0U, model.GetCursorPosition()); MoveCursorTo(model, 2); EXPECT_EQ(2U, model.GetCursorPosition()); model.InsertChar('a'); - EXPECT_EQ(WideToUTF16( + EXPECT_EQ(base::WideToUTF16( L"\x0915\x093f\x0061\x0915\x094d\x0915\x0915\x094d\x092e\x094d"), - model.GetText()); + model.text()); // ReplaceChar will replace the whole grapheme. model.ReplaceChar('b'); @@ -179,9 +172,9 @@ TEST_F(TextfieldViewsModelTest, EditString_ComplexScript) { // characters turned into empty square due to font regression. So, not able // to test 2 characters belong to the same grapheme. #if defined(OS_LINUX) - EXPECT_EQ(WideToUTF16( + EXPECT_EQ(base::WideToUTF16( L"\x0915\x093f\x0061\x0062\x0915\x0915\x094d\x092e\x094d"), - model.GetText()); + model.text()); #endif EXPECT_EQ(4U, model.GetCursorPosition()); } @@ -193,22 +186,20 @@ TEST_F(TextfieldViewsModelTest, EditString_ComplexScript) { // to test 2 characters belong to the same grapheme. #if defined(OS_LINUX) EXPECT_TRUE(model.Delete()); - EXPECT_EQ(WideToUTF16(L"\x0061\x0062\x0915\x0915\x094d\x092e\x094d"), - model.GetText()); - MoveCursorTo(model, model.GetText().length()); - EXPECT_EQ(model.GetText().length(), model.GetCursorPosition()); + EXPECT_EQ(base::WideToUTF16(L"\x0061\x0062\x0915\x0915\x094d\x092e\x094d"), + model.text()); + MoveCursorTo(model, model.text().length()); + EXPECT_EQ(model.text().length(), model.GetCursorPosition()); EXPECT_TRUE(model.Backspace()); - EXPECT_EQ(WideToUTF16(L"\x0061\x0062\x0915\x0915\x094d\x092e"), - model.GetText()); + EXPECT_EQ(base::WideToUTF16(L"\x0061\x0062\x0915\x0915\x094d\x092e"), + model.text()); #endif // Test cursor position and deletion for Hindi Virama. - model.SetText(WideToUTF16(L"\x0D38\x0D4D\x0D15\x0D16\x0D2E")); + model.SetText(base::WideToUTF16(L"\x0D38\x0D4D\x0D15\x0D16\x0D2E")); MoveCursorTo(model, 0); EXPECT_EQ(0U, model.GetCursorPosition()); - // TODO(asvitkine): Disable tests that fail on XP bots due to lack of complete - // font support for some scripts - http://crbug.com/106450 if (!on_windows_xp) { MoveCursorTo(model, 1); EXPECT_EQ(0U, model.GetCursorPosition()); @@ -222,44 +213,46 @@ TEST_F(TextfieldViewsModelTest, EditString_ComplexScript) { MoveCursorTo(model, 2); EXPECT_EQ(2U, model.GetCursorPosition()); EXPECT_TRUE(model.Backspace()); - EXPECT_EQ(WideToUTF16(L"\x0D38\x0D15\x0D16\x0D2E"), model.GetText()); + EXPECT_EQ(base::WideToUTF16(L"\x0D38\x0D15\x0D16\x0D2E"), model.text()); #endif - model.SetText(WideToUTF16(L"\x05d5\x05b7\x05D9\x05B0\x05D4\x05B4\x05D9")); + model.SetText( + base::WideToUTF16(L"\x05d5\x05b7\x05D9\x05B0\x05D4\x05B4\x05D9")); MoveCursorTo(model, 0); EXPECT_TRUE(model.Delete()); EXPECT_TRUE(model.Delete()); EXPECT_TRUE(model.Delete()); EXPECT_TRUE(model.Delete()); - EXPECT_EQ(WideToUTF16(L""), model.GetText()); + EXPECT_EQ(base::WideToUTF16(L""), model.text()); // The first 2 characters are not strong directionality characters. - model.SetText(WideToUTF16(L"\x002C\x0020\x05D1\x05BC\x05B7\x05E9\x05BC")); + model.SetText( + base::WideToUTF16(L"\x002C\x0020\x05D1\x05BC\x05B7\x05E9\x05BC")); model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false); EXPECT_TRUE(model.Backspace()); - EXPECT_EQ(WideToUTF16(L"\x002C\x0020\x05D1\x05BC\x05B7\x05E9"), - model.GetText()); + EXPECT_EQ(base::WideToUTF16(L"\x002C\x0020\x05D1\x05BC\x05B7\x05E9"), + model.text()); } -TEST_F(TextfieldViewsModelTest, EmptyString) { - TextfieldViewsModel model(NULL); - EXPECT_EQ(string16(), model.GetText()); - EXPECT_EQ(string16(), model.GetSelectedText()); +TEST_F(TextfieldModelTest, EmptyString) { + TextfieldModel model(NULL); + EXPECT_EQ(base::string16(), model.text()); + EXPECT_EQ(base::string16(), model.GetSelectedText()); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); EXPECT_EQ(0U, model.GetCursorPosition()); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); EXPECT_EQ(0U, model.GetCursorPosition()); - EXPECT_EQ(string16(), model.GetSelectedText()); + EXPECT_EQ(base::string16(), model.GetSelectedText()); EXPECT_FALSE(model.Delete()); EXPECT_FALSE(model.Backspace()); } -TEST_F(TextfieldViewsModelTest, Selection) { - TextfieldViewsModel model(NULL); - model.Append(ASCIIToUTF16("HELLO")); +TEST_F(TextfieldModelTest, Selection) { + TextfieldModel model(NULL); + model.Append(base::ASCIIToUTF16("HELLO")); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); EXPECT_STR_EQ("E", model.GetSelectedText()); @@ -271,7 +264,7 @@ TEST_F(TextfieldViewsModelTest, Selection) { model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, true); EXPECT_STR_EQ("ELLO", model.GetSelectedText()); model.ClearSelection(); - EXPECT_EQ(string16(), model.GetSelectedText()); + EXPECT_EQ(base::string16(), model.GetSelectedText()); // SelectAll(false) selects towards the end. model.SelectAll(false); @@ -301,122 +294,123 @@ TEST_F(TextfieldViewsModelTest, Selection) { EXPECT_EQ(5U, model.GetCursorPosition()); } -TEST_F(TextfieldViewsModelTest, Selection_BidiWithNonSpacingMarks) { +TEST_F(TextfieldModelTest, Selection_BidiWithNonSpacingMarks) { // Selection is a logical operation. And it should work with the arrow // keys doing visual movements, while the selection is logical between // the (logical) start and end points. Selection is simply defined as // the portion of text between the logical positions of the start and end // caret positions. - TextfieldViewsModel model(NULL); + TextfieldModel model(NULL); // TODO(xji): temporarily disable in platform Win since the complex script // characters turned into empty square due to font regression. So, not able // to test 2 characters belong to the same grapheme. #if defined(OS_LINUX) - model.Append(WideToUTF16( + model.Append(base::WideToUTF16( L"abc\x05E9\x05BC\x05C1\x05B8\x05E0\x05B8" L"def")); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); EXPECT_EQ(gfx::Range(2, 3), model.render_text()->selection()); - EXPECT_EQ(WideToUTF16(L"c"), model.GetSelectedText()); + EXPECT_EQ(base::WideToUTF16(L"c"), model.GetSelectedText()); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); EXPECT_EQ(gfx::Range(2, 7), model.render_text()->selection()); - EXPECT_EQ(WideToUTF16(L"c\x05E9\x05BC\x05C1\x05B8"), + EXPECT_EQ(base::WideToUTF16(L"c\x05E9\x05BC\x05C1\x05B8"), model.GetSelectedText()); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); EXPECT_EQ(gfx::Range(2, 3), model.render_text()->selection()); - EXPECT_EQ(WideToUTF16(L"c"), model.GetSelectedText()); + EXPECT_EQ(base::WideToUTF16(L"c"), model.GetSelectedText()); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); EXPECT_EQ(gfx::Range(2, 10), model.render_text()->selection()); - EXPECT_EQ(WideToUTF16(L"c\x05E9\x05BC\x05C1\x05B8\x05E0\x05B8" L"d"), + EXPECT_EQ(base::WideToUTF16(L"c\x05E9\x05BC\x05C1\x05B8\x05E0\x05B8" L"d"), model.GetSelectedText()); model.ClearSelection(); - EXPECT_EQ(string16(), model.GetSelectedText()); + EXPECT_EQ(base::string16(), model.GetSelectedText()); model.SelectAll(false); - EXPECT_EQ(WideToUTF16(L"abc\x05E9\x05BC\x05C1\x05B8\x05E0\x05B8" L"def"), - model.GetSelectedText()); + EXPECT_EQ( + base::WideToUTF16(L"abc\x05E9\x05BC\x05C1\x05B8\x05E0\x05B8" L"def"), + model.GetSelectedText()); #endif // In case of "aBc", this test shows how to select "aB" or "Bc", assume 'B' is // an RTL character. - model.SetText(WideToUTF16(L"a\x05E9" L"b")); + model.SetText(base::WideToUTF16(L"a\x05E9" L"b")); MoveCursorTo(model, 0); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); - EXPECT_EQ(WideToUTF16(L"a"), model.GetSelectedText()); + EXPECT_EQ(base::WideToUTF16(L"a"), model.GetSelectedText()); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); - EXPECT_EQ(WideToUTF16(L"a"), model.GetSelectedText()); + EXPECT_EQ(base::WideToUTF16(L"a"), model.GetSelectedText()); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); - EXPECT_EQ(WideToUTF16(L"a\x05E9" L"b"), model.GetSelectedText()); + EXPECT_EQ(base::WideToUTF16(L"a\x05E9" L"b"), model.GetSelectedText()); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); EXPECT_EQ(3U, model.GetCursorPosition()); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); - EXPECT_EQ(WideToUTF16(L"b"), model.GetSelectedText()); + EXPECT_EQ(base::WideToUTF16(L"b"), model.GetSelectedText()); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); - EXPECT_EQ(WideToUTF16(L"b"), model.GetSelectedText()); + EXPECT_EQ(base::WideToUTF16(L"b"), model.GetSelectedText()); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); - EXPECT_EQ(WideToUTF16(L"a\x05E9" L"b"), model.GetSelectedText()); + EXPECT_EQ(base::WideToUTF16(L"a\x05E9" L"b"), model.GetSelectedText()); model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); - EXPECT_EQ(WideToUTF16(L"a\x05E9"), model.GetSelectedText()); + EXPECT_EQ(base::WideToUTF16(L"a\x05E9"), model.GetSelectedText()); model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); - EXPECT_EQ(WideToUTF16(L"\x05E9" L"b"), model.GetSelectedText()); + EXPECT_EQ(base::WideToUTF16(L"\x05E9" L"b"), model.GetSelectedText()); model.ClearSelection(); - EXPECT_EQ(string16(), model.GetSelectedText()); + EXPECT_EQ(base::string16(), model.GetSelectedText()); model.SelectAll(false); - EXPECT_EQ(WideToUTF16(L"a\x05E9" L"b"), model.GetSelectedText()); + EXPECT_EQ(base::WideToUTF16(L"a\x05E9" L"b"), model.GetSelectedText()); } -TEST_F(TextfieldViewsModelTest, SelectionAndEdit) { - TextfieldViewsModel model(NULL); - model.Append(ASCIIToUTF16("HELLO")); +TEST_F(TextfieldModelTest, SelectionAndEdit) { + TextfieldModel model(NULL); + model.Append(base::ASCIIToUTF16("HELLO")); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); // "EL" EXPECT_TRUE(model.Backspace()); - EXPECT_STR_EQ("HLO", model.GetText()); + EXPECT_STR_EQ("HLO", model.text()); - model.Append(ASCIIToUTF16("ILL")); + model.Append(base::ASCIIToUTF16("ILL")); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); // "LO" EXPECT_TRUE(model.Delete()); - EXPECT_STR_EQ("HILL", model.GetText()); + EXPECT_STR_EQ("HILL", model.text()); EXPECT_EQ(1U, model.GetCursorPosition()); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); // "I" model.InsertChar('E'); - EXPECT_STR_EQ("HELL", model.GetText()); + EXPECT_STR_EQ("HELL", model.text()); model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); // "H" model.ReplaceChar('B'); - EXPECT_STR_EQ("BELL", model.GetText()); + EXPECT_STR_EQ("BELL", model.text()); model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); // "ELL" model.ReplaceChar('E'); - EXPECT_STR_EQ("BEE", model.GetText()); + EXPECT_STR_EQ("BEE", model.text()); } -TEST_F(TextfieldViewsModelTest, Word) { - TextfieldViewsModel model(NULL); +TEST_F(TextfieldModelTest, Word) { + TextfieldModel model(NULL); model.Append( - ASCIIToUTF16("The answer to Life, the Universe, and Everything")); + base::ASCIIToUTF16("The answer to Life, the Universe, and Everything")); model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, false); EXPECT_EQ(3U, model.GetCursorPosition()); model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, false); @@ -456,16 +450,16 @@ TEST_F(TextfieldViewsModelTest, Word) { model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true); EXPECT_STR_EQ("The answer to Life", model.GetSelectedText()); model.ReplaceChar('4'); - EXPECT_EQ(string16(), model.GetSelectedText()); - EXPECT_STR_EQ("42", model.GetText()); + EXPECT_EQ(base::string16(), model.GetSelectedText()); + EXPECT_STR_EQ("42", model.text()); } -TEST_F(TextfieldViewsModelTest, SetText) { - TextfieldViewsModel model(NULL); - model.Append(ASCIIToUTF16("HELLO")); +TEST_F(TextfieldModelTest, SetText) { + TextfieldModel model(NULL); + model.Append(base::ASCIIToUTF16("HELLO")); model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); - model.SetText(ASCIIToUTF16("GOODBYE")); - EXPECT_STR_EQ("GOODBYE", model.GetText()); + model.SetText(base::ASCIIToUTF16("GOODBYE")); + EXPECT_STR_EQ("GOODBYE", model.text()); // SetText move the cursor to the end of the new text. EXPECT_EQ(7U, model.GetCursorPosition()); model.SelectAll(false); @@ -473,37 +467,38 @@ TEST_F(TextfieldViewsModelTest, SetText) { model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); EXPECT_EQ(7U, model.GetCursorPosition()); - model.SetText(ASCIIToUTF16("BYE")); + model.SetText(base::ASCIIToUTF16("BYE")); // Setting shorter string moves the cursor to the end of the new string. EXPECT_EQ(3U, model.GetCursorPosition()); - EXPECT_EQ(string16(), model.GetSelectedText()); - model.SetText(string16()); + EXPECT_EQ(base::string16(), model.GetSelectedText()); + model.SetText(base::string16()); EXPECT_EQ(0U, model.GetCursorPosition()); } -TEST_F(TextfieldViewsModelTest, Clipboard) { +TEST_F(TextfieldModelTest, Clipboard) { ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); - const string16 initial_clipboard_text = ASCIIToUTF16("initial text"); + const base::string16 initial_clipboard_text = + base::ASCIIToUTF16("initial text"); ui::ScopedClipboardWriter(clipboard, ui::CLIPBOARD_TYPE_COPY_PASTE). WriteText(initial_clipboard_text); - string16 clipboard_text; - TextfieldViewsModel model(NULL); - model.Append(ASCIIToUTF16("HELLO WORLD")); + base::string16 clipboard_text; + TextfieldModel model(NULL); + model.Append(base::ASCIIToUTF16("HELLO WORLD")); // Cut with an empty selection should do nothing. model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); EXPECT_FALSE(model.Cut()); clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &clipboard_text); EXPECT_EQ(initial_clipboard_text, clipboard_text); - EXPECT_STR_EQ("HELLO WORLD", model.GetText()); + EXPECT_STR_EQ("HELLO WORLD", model.text()); EXPECT_EQ(11U, model.GetCursorPosition()); // Copy with an empty selection should do nothing. model.Copy(); clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &clipboard_text); EXPECT_EQ(initial_clipboard_text, clipboard_text); - EXPECT_STR_EQ("HELLO WORLD", model.GetText()); + EXPECT_STR_EQ("HELLO WORLD", model.text()); EXPECT_EQ(11U, model.GetCursorPosition()); // Cut on obscured (password) text should do nothing. @@ -512,15 +507,15 @@ TEST_F(TextfieldViewsModelTest, Clipboard) { EXPECT_FALSE(model.Cut()); clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &clipboard_text); EXPECT_EQ(initial_clipboard_text, clipboard_text); - EXPECT_STR_EQ("HELLO WORLD", model.GetText()); + EXPECT_STR_EQ("HELLO WORLD", model.text()); EXPECT_STR_EQ("HELLO WORLD", model.GetSelectedText()); - // Copy on obscured text should do nothing. + // Copy on obscured (password) text should do nothing. model.SelectAll(false); EXPECT_FALSE(model.Copy()); clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &clipboard_text); EXPECT_EQ(initial_clipboard_text, clipboard_text); - EXPECT_STR_EQ("HELLO WORLD", model.GetText()); + EXPECT_STR_EQ("HELLO WORLD", model.text()); EXPECT_STR_EQ("HELLO WORLD", model.GetSelectedText()); // Cut with non-empty selection. @@ -530,7 +525,7 @@ TEST_F(TextfieldViewsModelTest, Clipboard) { EXPECT_TRUE(model.Cut()); clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &clipboard_text); EXPECT_STR_EQ("WORLD", clipboard_text); - EXPECT_STR_EQ("HELLO ", model.GetText()); + EXPECT_STR_EQ("HELLO ", model.text()); EXPECT_EQ(6U, model.GetCursorPosition()); // Copy with non-empty selection. @@ -538,69 +533,71 @@ TEST_F(TextfieldViewsModelTest, Clipboard) { EXPECT_TRUE(model.Copy()); clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &clipboard_text); EXPECT_STR_EQ("HELLO ", clipboard_text); - EXPECT_STR_EQ("HELLO ", model.GetText()); + EXPECT_STR_EQ("HELLO ", model.text()); EXPECT_EQ(6U, model.GetCursorPosition()); // Test that paste works regardless of the obscured bit. model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); EXPECT_TRUE(model.Paste()); - EXPECT_STR_EQ("HELLO HELLO ", model.GetText()); + EXPECT_STR_EQ("HELLO HELLO ", model.text()); EXPECT_EQ(12U, model.GetCursorPosition()); model.render_text()->SetObscured(true); EXPECT_TRUE(model.Paste()); - EXPECT_STR_EQ("HELLO HELLO HELLO ", model.GetText()); + EXPECT_STR_EQ("HELLO HELLO HELLO ", model.text()); EXPECT_EQ(18U, model.GetCursorPosition()); } -static void SelectWordTestVerifier(const TextfieldViewsModel& model, - const string16 &expected_selected_string, size_t expected_cursor_pos) { +static void SelectWordTestVerifier( + const TextfieldModel& model, + const base::string16 &expected_selected_string, + size_t expected_cursor_pos) { EXPECT_EQ(expected_selected_string, model.GetSelectedText()); EXPECT_EQ(expected_cursor_pos, model.GetCursorPosition()); } -TEST_F(TextfieldViewsModelTest, SelectWordTest) { - TextfieldViewsModel model(NULL); - model.Append(ASCIIToUTF16(" HELLO !! WO RLD ")); +TEST_F(TextfieldModelTest, SelectWordTest) { + TextfieldModel model(NULL); + model.Append(base::ASCIIToUTF16(" HELLO !! WO RLD ")); // Test when cursor is at the beginning. model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false); model.SelectWord(); - SelectWordTestVerifier(model, ASCIIToUTF16(" "), 2U); + SelectWordTestVerifier(model, base::ASCIIToUTF16(" "), 2U); // Test when cursor is at the beginning of a word. MoveCursorTo(model, 2); model.SelectWord(); - SelectWordTestVerifier(model, ASCIIToUTF16("HELLO"), 7U); + SelectWordTestVerifier(model, base::ASCIIToUTF16("HELLO"), 7U); // Test when cursor is at the end of a word. MoveCursorTo(model, 15); model.SelectWord(); - SelectWordTestVerifier(model, ASCIIToUTF16(" "), 20U); + SelectWordTestVerifier(model, base::ASCIIToUTF16(" "), 20U); // Test when cursor is somewhere in a non-alpha-numeric fragment. for (size_t cursor_pos = 8; cursor_pos < 13U; cursor_pos++) { MoveCursorTo(model, cursor_pos); model.SelectWord(); - SelectWordTestVerifier(model, ASCIIToUTF16(" !! "), 13U); + SelectWordTestVerifier(model, base::ASCIIToUTF16(" !! "), 13U); } // Test when cursor is somewhere in a whitespace fragment. MoveCursorTo(model, 17); model.SelectWord(); - SelectWordTestVerifier(model, ASCIIToUTF16(" "), 20U); + SelectWordTestVerifier(model, base::ASCIIToUTF16(" "), 20U); // Test when cursor is at the end. model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); model.SelectWord(); - SelectWordTestVerifier(model, ASCIIToUTF16(" "), 24U); + SelectWordTestVerifier(model, base::ASCIIToUTF16(" "), 24U); } // TODO(xji): temporarily disable in platform Win since the complex script // characters and Chinese characters are turned into empty square due to font // regression. #if defined(OS_LINUX) -TEST_F(TextfieldViewsModelTest, SelectWordTest_MixScripts) { - TextfieldViewsModel model(NULL); +TEST_F(TextfieldModelTest, SelectWordTest_MixScripts) { + TextfieldModel model(NULL); std::vector<WordAndCursor> word_and_cursor; word_and_cursor.push_back(WordAndCursor(L"a\x05d0", 2)); word_and_cursor.push_back(WordAndCursor(L"a\x05d0", 2)); @@ -617,22 +614,22 @@ TEST_F(TextfieldViewsModelTest, SelectWordTest_MixScripts) { word_and_cursor.push_back(WordAndCursor(L"\x5929", 14)); // The text consists of Ascii, Hebrew, Hindi with Virama sign, and Chinese. - model.SetText(WideToUTF16(L"a\x05d0 \x05d1\x05d2 \x0915\x094d\x0915 " - L"\x4E2D\x56FD\x82B1\x5929")); + model.SetText(base::WideToUTF16(L"a\x05d0 \x05d1\x05d2 \x0915\x094d\x0915 " + L"\x4E2D\x56FD\x82B1\x5929")); for (size_t i = 0; i < word_and_cursor.size(); ++i) { model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false); for (size_t j = 0; j < i; ++j) model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); model.SelectWord(); - SelectWordTestVerifier(model, WideToUTF16(word_and_cursor[i].word), + SelectWordTestVerifier(model, base::WideToUTF16(word_and_cursor[i].word), word_and_cursor[i].cursor); } } #endif -TEST_F(TextfieldViewsModelTest, RangeTest) { - TextfieldViewsModel model(NULL); - model.Append(ASCIIToUTF16("HELLO WORLD")); +TEST_F(TextfieldModelTest, RangeTest) { + TextfieldModel model(NULL); + model.Append(base::ASCIIToUTF16("HELLO WORLD")); model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false); gfx::Range range = model.render_text()->selection(); EXPECT_TRUE(range.is_empty()); @@ -694,9 +691,9 @@ TEST_F(TextfieldViewsModelTest, RangeTest) { EXPECT_EQ(0U, range.end()); } -TEST_F(TextfieldViewsModelTest, SelectRangeTest) { - TextfieldViewsModel model(NULL); - model.Append(ASCIIToUTF16("HELLO WORLD")); +TEST_F(TextfieldModelTest, SelectRangeTest) { + TextfieldModel model(NULL); + model.Append(base::ASCIIToUTF16("HELLO WORLD")); gfx::Range range(0, 6); EXPECT_FALSE(range.is_reversed()); model.SelectRange(range); @@ -738,9 +735,9 @@ TEST_F(TextfieldViewsModelTest, SelectRangeTest) { EXPECT_TRUE(model.GetSelectedText().empty()); } -TEST_F(TextfieldViewsModelTest, SelectionTest) { - TextfieldViewsModel model(NULL); - model.Append(ASCIIToUTF16("HELLO WORLD")); +TEST_F(TextfieldModelTest, SelectionTest) { + TextfieldModel model(NULL); + model.Append(base::ASCIIToUTF16("HELLO WORLD")); model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false); gfx::Range selection = model.render_text()->selection(); EXPECT_EQ(gfx::Range(0), selection); @@ -780,9 +777,9 @@ TEST_F(TextfieldViewsModelTest, SelectionTest) { EXPECT_EQ(gfx::Range(11, 0), selection); } -TEST_F(TextfieldViewsModelTest, SelectSelectionModelTest) { - TextfieldViewsModel model(NULL); - model.Append(ASCIIToUTF16("HELLO WORLD")); +TEST_F(TextfieldModelTest, SelectSelectionModelTest) { + TextfieldModel model(NULL); + model.Append(base::ASCIIToUTF16("HELLO WORLD")); model.SelectSelectionModel(gfx::SelectionModel(gfx::Range(0, 6), gfx::CURSOR_BACKWARD)); EXPECT_STR_EQ("HELLO ", model.GetSelectedText()); @@ -813,9 +810,9 @@ TEST_F(TextfieldViewsModelTest, SelectSelectionModelTest) { EXPECT_TRUE(model.GetSelectedText().empty()); } -TEST_F(TextfieldViewsModelTest, CompositionTextTest) { - TextfieldViewsModel model(this); - model.Append(ASCIIToUTF16("1234590")); +TEST_F(TextfieldModelTest, CompositionTextTest) { + TextfieldModel model(this); + model.Append(base::ASCIIToUTF16("1234590")); model.SelectRange(gfx::Range(5, 5)); EXPECT_FALSE(model.HasSelection()); EXPECT_EQ(5U, model.GetCursorPosition()); @@ -825,7 +822,7 @@ TEST_F(TextfieldViewsModelTest, CompositionTextTest) { EXPECT_EQ(gfx::Range(0, 7), range); ui::CompositionText composition; - composition.text = ASCIIToUTF16("678"); + composition.text = base::ASCIIToUTF16("678"); composition.underlines.push_back(ui::CompositionUnderline(0, 3, 0, false)); // Cursor should be at the end of composition when characters are just typed. @@ -834,7 +831,7 @@ TEST_F(TextfieldViewsModelTest, CompositionTextTest) { EXPECT_TRUE(model.HasCompositionText()); EXPECT_FALSE(model.HasSelection()); - // Cancel composition + // Cancel the composition. model.CancelCompositionText(); composition_text_confirmed_or_cleared_ = false; @@ -850,11 +847,11 @@ TEST_F(TextfieldViewsModelTest, CompositionTextTest) { model.GetTextRange(&range); EXPECT_EQ(10U, range.end()); - EXPECT_STR_EQ("1234567890", model.GetText()); + EXPECT_STR_EQ("1234567890", model.text()); model.GetCompositionTextRange(&range); EXPECT_EQ(gfx::Range(5, 8), range); - // composition text + // Check the composition text. EXPECT_STR_EQ("456", model.GetTextFromRange(gfx::Range(3, 6))); EXPECT_EQ(gfx::Range(5, 7), model.render_text()->selection()); @@ -867,74 +864,74 @@ TEST_F(TextfieldViewsModelTest, CompositionTextTest) { EXPECT_EQ(5U, model.GetCursorPosition()); model.SetCompositionText(composition); - EXPECT_STR_EQ("1234567890", model.GetText()); - EXPECT_TRUE(model.SetText(ASCIIToUTF16("1234567890"))); + EXPECT_STR_EQ("1234567890", model.text()); + EXPECT_TRUE(model.SetText(base::ASCIIToUTF16("1234567890"))); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); model.SetCompositionText(composition); - EXPECT_STR_EQ("1234567890678", model.GetText()); + EXPECT_STR_EQ("1234567890678", model.text()); - model.InsertText(UTF8ToUTF16("-")); + model.InsertText(base::UTF8ToUTF16("-")); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("1234567890-", model.GetText()); + EXPECT_STR_EQ("1234567890-", model.text()); EXPECT_FALSE(model.HasCompositionText()); EXPECT_FALSE(model.HasSelection()); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); EXPECT_STR_EQ("-", model.GetSelectedText()); model.SetCompositionText(composition); - EXPECT_STR_EQ("1234567890678", model.GetText()); + EXPECT_STR_EQ("1234567890678", model.text()); - model.ReplaceText(UTF8ToUTF16("-")); + model.ReplaceText(base::UTF8ToUTF16("-")); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("1234567890-", model.GetText()); + EXPECT_STR_EQ("1234567890-", model.text()); EXPECT_FALSE(model.HasCompositionText()); EXPECT_FALSE(model.HasSelection()); model.SetCompositionText(composition); - model.Append(UTF8ToUTF16("-")); + model.Append(base::UTF8ToUTF16("-")); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("1234567890-678-", model.GetText()); + EXPECT_STR_EQ("1234567890-678-", model.text()); model.SetCompositionText(composition); model.Delete(); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("1234567890-678-", model.GetText()); + EXPECT_STR_EQ("1234567890-678-", model.text()); model.SetCompositionText(composition); model.Backspace(); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("1234567890-678-", model.GetText()); + EXPECT_STR_EQ("1234567890-678-", model.text()); - model.SetText(string16()); + model.SetText(base::string16()); model.SetCompositionText(composition); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, false); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("678", model.GetText()); + EXPECT_STR_EQ("678", model.text()); EXPECT_EQ(2U, model.GetCursorPosition()); model.SetCompositionText(composition); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("676788", model.GetText()); + EXPECT_STR_EQ("676788", model.text()); EXPECT_EQ(6U, model.GetCursorPosition()); model.SetCompositionText(composition); model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, false); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("676788678", model.GetText()); + EXPECT_STR_EQ("676788678", model.text()); - model.SetText(string16()); + model.SetText(base::string16()); model.SetCompositionText(composition); model.MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, false); EXPECT_TRUE(composition_text_confirmed_or_cleared_); @@ -944,13 +941,13 @@ TEST_F(TextfieldViewsModelTest, CompositionTextTest) { model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, true); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("678678", model.GetText()); + EXPECT_STR_EQ("678678", model.text()); model.SetCompositionText(composition); model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("678", model.GetText()); + EXPECT_STR_EQ("678", model.text()); model.SetCompositionText(composition); gfx::SelectionModel sel( @@ -959,25 +956,25 @@ TEST_F(TextfieldViewsModelTest, CompositionTextTest) { model.MoveCursorTo(sel); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("678678", model.GetText()); + EXPECT_STR_EQ("678678", model.text()); model.SetCompositionText(composition); model.SelectRange(gfx::Range(0, 3)); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("678", model.GetText()); + EXPECT_STR_EQ("678", model.text()); model.SetCompositionText(composition); model.SelectAll(false); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("678", model.GetText()); + EXPECT_STR_EQ("678", model.text()); model.SetCompositionText(composition); model.SelectWord(); EXPECT_TRUE(composition_text_confirmed_or_cleared_); composition_text_confirmed_or_cleared_ = false; - EXPECT_STR_EQ("678", model.GetText()); + EXPECT_STR_EQ("678", model.text()); model.SetCompositionText(composition); model.ClearSelection(); @@ -989,528 +986,497 @@ TEST_F(TextfieldViewsModelTest, CompositionTextTest) { EXPECT_FALSE(composition_text_confirmed_or_cleared_); } -TEST_F(TextfieldViewsModelTest, UndoRedo_BasicTest) { - TextfieldViewsModel model(NULL); +TEST_F(TextfieldModelTest, UndoRedo_BasicTest) { + TextfieldModel model(NULL); model.InsertChar('a'); - EXPECT_FALSE(model.Redo()); // nothing to redo + EXPECT_FALSE(model.Redo()); // There is nothing to redo. EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("", model.GetText()); + EXPECT_STR_EQ("", model.text()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("a", model.GetText()); + EXPECT_STR_EQ("a", model.text()); // Continuous inserts are treated as one edit. model.InsertChar('b'); model.InsertChar('c'); - EXPECT_STR_EQ("abc", model.GetText()); + EXPECT_STR_EQ("abc", model.text()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("a", model.GetText()); + EXPECT_STR_EQ("a", model.text()); EXPECT_EQ(1U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("", model.GetText()); + EXPECT_STR_EQ("", model.text()); EXPECT_EQ(0U, model.GetCursorPosition()); // Undoing further shouldn't change the text. EXPECT_FALSE(model.Undo()); - EXPECT_STR_EQ("", model.GetText()); + EXPECT_STR_EQ("", model.text()); EXPECT_FALSE(model.Undo()); - EXPECT_STR_EQ("", model.GetText()); + EXPECT_STR_EQ("", model.text()); EXPECT_EQ(0U, model.GetCursorPosition()); // Redoing to the latest text. EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("a", model.GetText()); + EXPECT_STR_EQ("a", model.text()); EXPECT_EQ(1U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("abc", model.GetText()); + EXPECT_STR_EQ("abc", model.text()); EXPECT_EQ(3U, model.GetCursorPosition()); // Backspace =============================== EXPECT_TRUE(model.Backspace()); - EXPECT_STR_EQ("ab", model.GetText()); + EXPECT_STR_EQ("ab", model.text()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("abc", model.GetText()); + EXPECT_STR_EQ("abc", model.text()); EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ab", model.GetText()); + EXPECT_STR_EQ("ab", model.text()); EXPECT_EQ(2U, model.GetCursorPosition()); // Continous backspaces are treated as one edit. EXPECT_TRUE(model.Backspace()); EXPECT_TRUE(model.Backspace()); - EXPECT_STR_EQ("", model.GetText()); + EXPECT_STR_EQ("", model.text()); // Extra backspace shouldn't affect the history. EXPECT_FALSE(model.Backspace()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ab", model.GetText()); + EXPECT_STR_EQ("ab", model.text()); EXPECT_EQ(2U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("abc", model.GetText()); + EXPECT_STR_EQ("abc", model.text()); EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("a", model.GetText()); + EXPECT_STR_EQ("a", model.text()); EXPECT_EQ(1U, model.GetCursorPosition()); // Clear history model.ClearEditHistory(); EXPECT_FALSE(model.Undo()); EXPECT_FALSE(model.Redo()); - EXPECT_STR_EQ("a", model.GetText()); + EXPECT_STR_EQ("a", model.text()); EXPECT_EQ(1U, model.GetCursorPosition()); // Delete =============================== - model.SetText(ASCIIToUTF16("ABCDE")); + model.SetText(base::ASCIIToUTF16("ABCDE")); model.ClearEditHistory(); MoveCursorTo(model, 2); EXPECT_TRUE(model.Delete()); - EXPECT_STR_EQ("ABDE", model.GetText()); + EXPECT_STR_EQ("ABDE", model.text()); model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false); EXPECT_TRUE(model.Delete()); - EXPECT_STR_EQ("BDE", model.GetText()); + EXPECT_STR_EQ("BDE", model.text()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABDE", model.GetText()); + EXPECT_STR_EQ("ABDE", model.text()); EXPECT_EQ(0U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABCDE", model.GetText()); + EXPECT_STR_EQ("ABCDE", model.text()); EXPECT_EQ(2U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ABDE", model.GetText()); + EXPECT_STR_EQ("ABDE", model.text()); EXPECT_EQ(2U, model.GetCursorPosition()); // Continous deletes are treated as one edit. EXPECT_TRUE(model.Delete()); EXPECT_TRUE(model.Delete()); - EXPECT_STR_EQ("AB", model.GetText()); + EXPECT_STR_EQ("AB", model.text()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABDE", model.GetText()); + EXPECT_STR_EQ("ABDE", model.text()); EXPECT_EQ(2U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("AB", model.GetText()); + EXPECT_STR_EQ("AB", model.text()); EXPECT_EQ(2U, model.GetCursorPosition()); } -TEST_F(TextfieldViewsModelTest, UndoRedo_SetText) { +TEST_F(TextfieldModelTest, UndoRedo_SetText) { // This is to test the undo/redo behavior of omnibox. - TextfieldViewsModel model(NULL); + TextfieldModel model(NULL); model.InsertChar('w'); - EXPECT_STR_EQ("w", model.GetText()); + EXPECT_STR_EQ("w", model.text()); EXPECT_EQ(1U, model.GetCursorPosition()); - model.SetText(ASCIIToUTF16("www.google.com")); + model.SetText(base::ASCIIToUTF16("www.google.com")); EXPECT_EQ(14U, model.GetCursorPosition()); - EXPECT_STR_EQ("www.google.com", model.GetText()); + EXPECT_STR_EQ("www.google.com", model.text()); model.SelectRange(gfx::Range(14, 1)); model.InsertChar('w'); - EXPECT_STR_EQ("ww", model.GetText()); - model.SetText(ASCIIToUTF16("www.google.com")); + EXPECT_STR_EQ("ww", model.text()); + model.SetText(base::ASCIIToUTF16("www.google.com")); model.SelectRange(gfx::Range(14, 2)); model.InsertChar('w'); - EXPECT_STR_EQ("www", model.GetText()); - model.SetText(ASCIIToUTF16("www.google.com")); + EXPECT_STR_EQ("www", model.text()); + model.SetText(base::ASCIIToUTF16("www.google.com")); model.SelectRange(gfx::Range(14, 3)); model.InsertChar('.'); - EXPECT_STR_EQ("www.", model.GetText()); - model.SetText(ASCIIToUTF16("www.google.com")); + EXPECT_STR_EQ("www.", model.text()); + model.SetText(base::ASCIIToUTF16("www.google.com")); model.SelectRange(gfx::Range(14, 4)); model.InsertChar('y'); - EXPECT_STR_EQ("www.y", model.GetText()); - model.SetText(ASCIIToUTF16("www.youtube.com")); - EXPECT_STR_EQ("www.youtube.com", model.GetText()); + EXPECT_STR_EQ("www.y", model.text()); + model.SetText(base::ASCIIToUTF16("www.youtube.com")); + EXPECT_STR_EQ("www.youtube.com", model.text()); EXPECT_EQ(15U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("www.google.com", model.GetText()); + EXPECT_STR_EQ("www.google.com", model.text()); EXPECT_EQ(4U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("www.google.com", model.GetText()); + EXPECT_STR_EQ("www.google.com", model.text()); EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("www.google.com", model.GetText()); + EXPECT_STR_EQ("www.google.com", model.text()); EXPECT_EQ(2U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("www.google.com", model.GetText()); + EXPECT_STR_EQ("www.google.com", model.text()); EXPECT_EQ(1U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("", model.GetText()); + EXPECT_STR_EQ("", model.text()); EXPECT_EQ(0U, model.GetCursorPosition()); EXPECT_FALSE(model.Undo()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("www.google.com", model.GetText()); + EXPECT_STR_EQ("www.google.com", model.text()); EXPECT_EQ(1U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("www.google.com", model.GetText()); + EXPECT_STR_EQ("www.google.com", model.text()); EXPECT_EQ(2U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("www.google.com", model.GetText()); + EXPECT_STR_EQ("www.google.com", model.text()); EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("www.google.com", model.GetText()); + EXPECT_STR_EQ("www.google.com", model.text()); EXPECT_EQ(4U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("www.youtube.com", model.GetText()); + EXPECT_STR_EQ("www.youtube.com", model.text()); EXPECT_EQ(5U, model.GetCursorPosition()); EXPECT_FALSE(model.Redo()); } -TEST_F(TextfieldViewsModelTest, UndoRedo_BackspaceThenSetText) { +TEST_F(TextfieldModelTest, UndoRedo_BackspaceThenSetText) { // This is to test the undo/redo behavior of omnibox. - TextfieldViewsModel model(NULL); + TextfieldModel model(NULL); model.InsertChar('w'); - EXPECT_STR_EQ("w", model.GetText()); + EXPECT_STR_EQ("w", model.text()); EXPECT_EQ(1U, model.GetCursorPosition()); - model.SetText(ASCIIToUTF16("www.google.com")); + model.SetText(base::ASCIIToUTF16("www.google.com")); EXPECT_EQ(14U, model.GetCursorPosition()); - EXPECT_STR_EQ("www.google.com", model.GetText()); - model.SetText(ASCIIToUTF16("www.google.com")); // Confirm the text. + EXPECT_STR_EQ("www.google.com", model.text()); + model.SetText(base::ASCIIToUTF16("www.google.com")); // Confirm the text. model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); EXPECT_EQ(14U, model.GetCursorPosition()); EXPECT_TRUE(model.Backspace()); EXPECT_TRUE(model.Backspace()); - EXPECT_STR_EQ("www.google.c", model.GetText()); - // Autocomplete sets the text - model.SetText(ASCIIToUTF16("www.google.com/search=www.google.c")); - EXPECT_STR_EQ("www.google.com/search=www.google.c", model.GetText()); + EXPECT_STR_EQ("www.google.c", model.text()); + // Autocomplete sets the text. + model.SetText(base::ASCIIToUTF16("www.google.com/search=www.google.c")); + EXPECT_STR_EQ("www.google.com/search=www.google.c", model.text()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("www.google.c", model.GetText()); + EXPECT_STR_EQ("www.google.c", model.text()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("www.google.com", model.GetText()); + EXPECT_STR_EQ("www.google.com", model.text()); } -TEST_F(TextfieldViewsModelTest, UndoRedo_CutCopyPasteTest) { - TextfieldViewsModel model(NULL); - model.SetText(ASCIIToUTF16("ABCDE")); - EXPECT_FALSE(model.Redo()); // nothing to redo - // Cut +TEST_F(TextfieldModelTest, UndoRedo_CutCopyPasteTest) { + TextfieldModel model(NULL); + model.SetText(base::ASCIIToUTF16("ABCDE")); + EXPECT_FALSE(model.Redo()); // There is nothing to redo. + // Test Cut. model.SelectRange(gfx::Range(1, 3)); model.Cut(); - EXPECT_STR_EQ("ADE", model.GetText()); + EXPECT_STR_EQ("ADE", model.text()); EXPECT_EQ(1U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABCDE", model.GetText()); + EXPECT_STR_EQ("ABCDE", model.text()); EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("", model.GetText()); + EXPECT_STR_EQ("", model.text()); EXPECT_EQ(0U, model.GetCursorPosition()); - EXPECT_FALSE(model.Undo()); // no more undo - EXPECT_STR_EQ("", model.GetText()); + EXPECT_FALSE(model.Undo()); // There is no more to undo. + EXPECT_STR_EQ("", model.text()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ABCDE", model.GetText()); + EXPECT_STR_EQ("ABCDE", model.text()); EXPECT_EQ(5U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ADE", model.GetText()); + EXPECT_STR_EQ("ADE", model.text()); EXPECT_EQ(1U, model.GetCursorPosition()); - EXPECT_FALSE(model.Redo()); // no more redo - EXPECT_STR_EQ("ADE", model.GetText()); + EXPECT_FALSE(model.Redo()); // There is no more to redo. + EXPECT_STR_EQ("ADE", model.text()); model.Paste(); model.Paste(); model.Paste(); - EXPECT_STR_EQ("ABCBCBCDE", model.GetText()); + EXPECT_STR_EQ("ABCBCBCDE", model.text()); EXPECT_EQ(7U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABCBCDE", model.GetText()); + EXPECT_STR_EQ("ABCBCDE", model.text()); EXPECT_EQ(5U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABCDE", model.GetText()); + EXPECT_STR_EQ("ABCDE", model.text()); EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ADE", model.GetText()); + EXPECT_STR_EQ("ADE", model.text()); EXPECT_EQ(1U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABCDE", model.GetText()); + EXPECT_STR_EQ("ABCDE", model.text()); EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("", model.GetText()); + EXPECT_STR_EQ("", model.text()); EXPECT_EQ(0U, model.GetCursorPosition()); EXPECT_FALSE(model.Undo()); - EXPECT_STR_EQ("", model.GetText()); + EXPECT_STR_EQ("", model.text()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ABCDE", model.GetText()); // Redoing SetText + EXPECT_STR_EQ("ABCDE", model.text()); EXPECT_EQ(5U, model.GetCursorPosition()); - // Redo + // Test Redo. EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ADE", model.GetText()); + EXPECT_STR_EQ("ADE", model.text()); EXPECT_EQ(1U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ABCDE", model.GetText()); + EXPECT_STR_EQ("ABCDE", model.text()); EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ABCBCDE", model.GetText()); + EXPECT_STR_EQ("ABCBCDE", model.text()); EXPECT_EQ(5U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ABCBCBCDE", model.GetText()); + EXPECT_STR_EQ("ABCBCBCDE", model.text()); EXPECT_EQ(7U, model.GetCursorPosition()); EXPECT_FALSE(model.Redo()); - // with SelectRange + // Test using SelectRange. model.SelectRange(gfx::Range(1, 3)); EXPECT_TRUE(model.Cut()); - EXPECT_STR_EQ("ABCBCDE", model.GetText()); + EXPECT_STR_EQ("ABCBCDE", model.text()); EXPECT_EQ(1U, model.GetCursorPosition()); model.SelectRange(gfx::Range(1, 1)); EXPECT_FALSE(model.Cut()); model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); EXPECT_TRUE(model.Paste()); - EXPECT_STR_EQ("ABCBCDEBC", model.GetText()); + EXPECT_STR_EQ("ABCBCDEBC", model.text()); EXPECT_EQ(9U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABCBCDE", model.GetText()); + EXPECT_STR_EQ("ABCBCDE", model.text()); EXPECT_EQ(7U, model.GetCursorPosition()); - // empty cut shouldn't create an edit. + // An empty cut shouldn't create an edit. EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABCBCBCDE", model.GetText()); + EXPECT_STR_EQ("ABCBCBCDE", model.text()); EXPECT_EQ(3U, model.GetCursorPosition()); - // Copy + // Test Copy. ResetModel(&model); - model.SetText(ASCIIToUTF16("12345")); - EXPECT_STR_EQ("12345", model.GetText()); + model.SetText(base::ASCIIToUTF16("12345")); + EXPECT_STR_EQ("12345", model.text()); EXPECT_EQ(5U, model.GetCursorPosition()); model.SelectRange(gfx::Range(1, 3)); - model.Copy(); // Copy "23" - EXPECT_STR_EQ("12345", model.GetText()); + model.Copy(); // Copy "23". + EXPECT_STR_EQ("12345", model.text()); EXPECT_EQ(3U, model.GetCursorPosition()); model.Paste(); // Paste "23" into "23". - EXPECT_STR_EQ("12345", model.GetText()); + EXPECT_STR_EQ("12345", model.text()); EXPECT_EQ(3U, model.GetCursorPosition()); model.Paste(); - EXPECT_STR_EQ("1232345", model.GetText()); + EXPECT_STR_EQ("1232345", model.text()); EXPECT_EQ(5U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("12345", model.GetText()); + EXPECT_STR_EQ("12345", model.text()); EXPECT_EQ(3U, model.GetCursorPosition()); - // TODO(oshima): We need to change the return type from bool to enum. + // TODO(oshima): Change the return type from bool to enum. EXPECT_FALSE(model.Undo()); // No text change. - EXPECT_STR_EQ("12345", model.GetText()); + EXPECT_STR_EQ("12345", model.text()); EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("", model.GetText()); + EXPECT_STR_EQ("", model.text()); EXPECT_FALSE(model.Undo()); - // Redo + // Test Redo. EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("12345", model.GetText()); + EXPECT_STR_EQ("12345", model.text()); EXPECT_EQ(5U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("12345", model.GetText()); // For 1st paste + EXPECT_STR_EQ("12345", model.text()); // For 1st paste EXPECT_EQ(3U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("1232345", model.GetText()); + EXPECT_STR_EQ("1232345", model.text()); EXPECT_EQ(5U, model.GetCursorPosition()); EXPECT_FALSE(model.Redo()); - EXPECT_STR_EQ("1232345", model.GetText()); + EXPECT_STR_EQ("1232345", model.text()); - // Test using SelectRange + // Test using SelectRange. model.SelectRange(gfx::Range(1, 3)); model.Copy(); - EXPECT_STR_EQ("1232345", model.GetText()); + EXPECT_STR_EQ("1232345", model.text()); model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); EXPECT_TRUE(model.Paste()); - EXPECT_STR_EQ("123234523", model.GetText()); + EXPECT_STR_EQ("123234523", model.text()); EXPECT_EQ(9U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("1232345", model.GetText()); + EXPECT_STR_EQ("1232345", model.text()); EXPECT_EQ(7U, model.GetCursorPosition()); } -TEST_F(TextfieldViewsModelTest, UndoRedo_CursorTest) { - TextfieldViewsModel model(NULL); +TEST_F(TextfieldModelTest, UndoRedo_CursorTest) { + TextfieldModel model(NULL); model.InsertChar('a'); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, false); model.MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); model.InsertChar('b'); // Moving the cursor shouldn't create a new edit. - EXPECT_STR_EQ("ab", model.GetText()); + EXPECT_STR_EQ("ab", model.text()); EXPECT_FALSE(model.Redo()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("", model.GetText()); + EXPECT_STR_EQ("", model.text()); EXPECT_FALSE(model.Undo()); - EXPECT_STR_EQ("", model.GetText()); + EXPECT_STR_EQ("", model.text()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ab", model.GetText()); + EXPECT_STR_EQ("ab", model.text()); EXPECT_EQ(2U, model.GetCursorPosition()); EXPECT_FALSE(model.Redo()); } -void RunInsertReplaceTest(TextfieldViewsModel& model) { +void RunInsertReplaceTest(TextfieldModel& model) { const bool reverse = model.render_text()->selection().is_reversed(); model.InsertChar('1'); model.InsertChar('2'); model.InsertChar('3'); - EXPECT_STR_EQ("a123d", model.GetText()); + EXPECT_STR_EQ("a123d", model.text()); EXPECT_EQ(4U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("abcd", model.GetText()); + EXPECT_STR_EQ("abcd", model.text()); EXPECT_EQ(reverse ? 1U : 3U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("", model.GetText()); + EXPECT_STR_EQ("", model.text()); EXPECT_EQ(0U, model.GetCursorPosition()); EXPECT_FALSE(model.Undo()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("abcd", model.GetText()); - EXPECT_EQ(4U, model.GetCursorPosition()); // By SetText + EXPECT_STR_EQ("abcd", model.text()); + EXPECT_EQ(4U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("a123d", model.GetText()); + EXPECT_STR_EQ("a123d", model.text()); EXPECT_EQ(4U, model.GetCursorPosition()); EXPECT_FALSE(model.Redo()); } -void RunOverwriteReplaceTest(TextfieldViewsModel& model) { +void RunOverwriteReplaceTest(TextfieldModel& model) { const bool reverse = model.render_text()->selection().is_reversed(); model.ReplaceChar('1'); model.ReplaceChar('2'); model.ReplaceChar('3'); model.ReplaceChar('4'); - EXPECT_STR_EQ("a1234", model.GetText()); + EXPECT_STR_EQ("a1234", model.text()); EXPECT_EQ(5U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("abcd", model.GetText()); + EXPECT_STR_EQ("abcd", model.text()); EXPECT_EQ(reverse ? 1U : 3U, model.GetCursorPosition()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("", model.GetText()); + EXPECT_STR_EQ("", model.text()); EXPECT_EQ(0U, model.GetCursorPosition()); EXPECT_FALSE(model.Undo()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("abcd", model.GetText()); + EXPECT_STR_EQ("abcd", model.text()); EXPECT_EQ(4U, model.GetCursorPosition()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("a1234", model.GetText()); + EXPECT_STR_EQ("a1234", model.text()); EXPECT_EQ(5U, model.GetCursorPosition()); EXPECT_FALSE(model.Redo()); } -TEST_F(TextfieldViewsModelTest, UndoRedo_ReplaceTest) { - // By Cursor - { - SCOPED_TRACE("forward & insert by cursor"); - TextfieldViewsModel model(NULL); - model.SetText(ASCIIToUTF16("abcd")); - model.SelectRange(gfx::Range(1, 3)); - RunInsertReplaceTest(model); - } - { - SCOPED_TRACE("backward & insert by cursor"); - TextfieldViewsModel model(NULL); - model.SetText(ASCIIToUTF16("abcd")); - model.SelectRange(gfx::Range(3, 1)); - RunInsertReplaceTest(model); - } - { - SCOPED_TRACE("forward & overwrite by cursor"); - TextfieldViewsModel model(NULL); - model.SetText(ASCIIToUTF16("abcd")); - model.SelectRange(gfx::Range(1, 3)); - RunOverwriteReplaceTest(model); - } - { - SCOPED_TRACE("backward & overwrite by cursor"); - TextfieldViewsModel model(NULL); - model.SetText(ASCIIToUTF16("abcd")); - model.SelectRange(gfx::Range(3, 1)); - RunOverwriteReplaceTest(model); - } - // By SelectRange API +TEST_F(TextfieldModelTest, UndoRedo_ReplaceTest) { { - SCOPED_TRACE("forward & insert by SelectRange"); - TextfieldViewsModel model(NULL); - model.SetText(ASCIIToUTF16("abcd")); + SCOPED_TRACE("Select forwards and insert."); + TextfieldModel model(NULL); + model.SetText(base::ASCIIToUTF16("abcd")); model.SelectRange(gfx::Range(1, 3)); RunInsertReplaceTest(model); } { - SCOPED_TRACE("backward & insert by SelectRange"); - TextfieldViewsModel model(NULL); - model.SetText(ASCIIToUTF16("abcd")); + SCOPED_TRACE("Select reversed and insert."); + TextfieldModel model(NULL); + model.SetText(base::ASCIIToUTF16("abcd")); model.SelectRange(gfx::Range(3, 1)); RunInsertReplaceTest(model); } { - SCOPED_TRACE("forward & overwrite by SelectRange"); - TextfieldViewsModel model(NULL); - model.SetText(ASCIIToUTF16("abcd")); + SCOPED_TRACE("Select forwards and overwrite."); + TextfieldModel model(NULL); + model.SetText(base::ASCIIToUTF16("abcd")); model.SelectRange(gfx::Range(1, 3)); RunOverwriteReplaceTest(model); } { - SCOPED_TRACE("backward & overwrite by SelectRange"); - TextfieldViewsModel model(NULL); - model.SetText(ASCIIToUTF16("abcd")); + SCOPED_TRACE("Select reversed and overwrite."); + TextfieldModel model(NULL); + model.SetText(base::ASCIIToUTF16("abcd")); model.SelectRange(gfx::Range(3, 1)); RunOverwriteReplaceTest(model); } } -TEST_F(TextfieldViewsModelTest, UndoRedo_CompositionText) { - TextfieldViewsModel model(NULL); +TEST_F(TextfieldModelTest, UndoRedo_CompositionText) { + TextfieldModel model(NULL); ui::CompositionText composition; - composition.text = ASCIIToUTF16("abc"); + composition.text = base::ASCIIToUTF16("abc"); composition.underlines.push_back(ui::CompositionUnderline(0, 3, 0, false)); composition.selection = gfx::Range(2, 3); - model.SetText(ASCIIToUTF16("ABCDE")); + model.SetText(base::ASCIIToUTF16("ABCDE")); model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); model.InsertChar('x'); - EXPECT_STR_EQ("ABCDEx", model.GetText()); + EXPECT_STR_EQ("ABCDEx", model.text()); EXPECT_TRUE(model.Undo()); // set composition should forget undone edit. model.SetCompositionText(composition); EXPECT_TRUE(model.HasCompositionText()); EXPECT_TRUE(model.HasSelection()); - EXPECT_STR_EQ("ABCDEabc", model.GetText()); + EXPECT_STR_EQ("ABCDEabc", model.text()); - // Accepting composition + // Confirm the composition. model.ConfirmCompositionText(); - EXPECT_STR_EQ("ABCDEabc", model.GetText()); + EXPECT_STR_EQ("ABCDEabc", model.text()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABCDE", model.GetText()); + EXPECT_STR_EQ("ABCDE", model.text()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("", model.GetText()); + EXPECT_STR_EQ("", model.text()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ABCDE", model.GetText()); + EXPECT_STR_EQ("ABCDE", model.text()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ABCDEabc", model.GetText()); + EXPECT_STR_EQ("ABCDEabc", model.text()); EXPECT_FALSE(model.Redo()); - // Canceling composition + // Cancel the composition. model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, false); model.SetCompositionText(composition); - EXPECT_STR_EQ("abcABCDEabc", model.GetText()); + EXPECT_STR_EQ("abcABCDEabc", model.text()); model.CancelCompositionText(); - EXPECT_STR_EQ("ABCDEabc", model.GetText()); + EXPECT_STR_EQ("ABCDEabc", model.text()); EXPECT_FALSE(model.Redo()); - EXPECT_STR_EQ("ABCDEabc", model.GetText()); + EXPECT_STR_EQ("ABCDEabc", model.text()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABCDE", model.GetText()); + EXPECT_STR_EQ("ABCDE", model.text()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ABCDEabc", model.GetText()); + EXPECT_STR_EQ("ABCDEabc", model.text()); EXPECT_FALSE(model.Redo()); - // SetText with the same text as the result. + // Call SetText with the same text as the result. ResetModel(&model); - model.SetText(ASCIIToUTF16("ABCDE")); + model.SetText(base::ASCIIToUTF16("ABCDE")); model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); model.SetCompositionText(composition); - EXPECT_STR_EQ("ABCDEabc", model.GetText()); - model.SetText(ASCIIToUTF16("ABCDEabc")); - EXPECT_STR_EQ("ABCDEabc", model.GetText()); + EXPECT_STR_EQ("ABCDEabc", model.text()); + model.SetText(base::ASCIIToUTF16("ABCDEabc")); + EXPECT_STR_EQ("ABCDEabc", model.text()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABCDE", model.GetText()); + EXPECT_STR_EQ("ABCDE", model.text()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("ABCDEabc", model.GetText()); + EXPECT_STR_EQ("ABCDEabc", model.text()); EXPECT_FALSE(model.Redo()); - // SetText with the different text than the result should not - // remember composition text. + // Call SetText with a different result; the composition should be forgotten. ResetModel(&model); - model.SetText(ASCIIToUTF16("ABCDE")); + model.SetText(base::ASCIIToUTF16("ABCDE")); model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, false); model.SetCompositionText(composition); - EXPECT_STR_EQ("ABCDEabc", model.GetText()); - model.SetText(ASCIIToUTF16("1234")); - EXPECT_STR_EQ("1234", model.GetText()); + EXPECT_STR_EQ("ABCDEabc", model.text()); + model.SetText(base::ASCIIToUTF16("1234")); + EXPECT_STR_EQ("1234", model.text()); EXPECT_TRUE(model.Undo()); - EXPECT_STR_EQ("ABCDE", model.GetText()); + EXPECT_STR_EQ("ABCDE", model.text()); EXPECT_TRUE(model.Redo()); - EXPECT_STR_EQ("1234", model.GetText()); + EXPECT_STR_EQ("1234", model.text()); EXPECT_FALSE(model.Redo()); - // TODO(oshima): We need MockInputMethod to test the behavior with IME. + // TODO(oshima): Test the behavior with an IME. } } // namespace views diff --git a/chromium/ui/views/controls/textfield/textfield_test_api.cc b/chromium/ui/views/controls/textfield/textfield_test_api.cc new file mode 100644 index 00000000000..be45d24cc42 --- /dev/null +++ b/chromium/ui/views/controls/textfield/textfield_test_api.cc @@ -0,0 +1,30 @@ +// Copyright 2014 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/controls/textfield/textfield_test_api.h" + +namespace views { + +TextfieldTestApi::TextfieldTestApi(Textfield* textfield) + : textfield_(textfield) { + DCHECK(textfield); +} + +void TextfieldTestApi::UpdateContextMenu() { + textfield_->UpdateContextMenu(); +} + +gfx::RenderText* TextfieldTestApi::GetRenderText() const { + return textfield_->GetRenderText(); +} + +void TextfieldTestApi::CreateTouchSelectionControllerAndNotifyIt() { + textfield_->CreateTouchSelectionControllerAndNotifyIt(); +} + +void TextfieldTestApi::ResetTouchSelectionController() { + textfield_->touch_selection_controller_.reset(); +} + +} // namespace views diff --git a/chromium/ui/views/controls/textfield/textfield_test_api.h b/chromium/ui/views/controls/textfield/textfield_test_api.h new file mode 100644 index 00000000000..1af2ffcdf62 --- /dev/null +++ b/chromium/ui/views/controls/textfield/textfield_test_api.h @@ -0,0 +1,43 @@ +// Copyright 2014 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_CONTROLS_TEXTFIELD_TEXTFIELD_TEST_API_H_ +#define UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_TEST_API_H_ + +#include "ui/views/controls/textfield/textfield.h" + +namespace views { + +// Helper class to access internal state of Textfield in tests. +class TextfieldTestApi { + public: + explicit TextfieldTestApi(Textfield* textfield); + + void UpdateContextMenu(); + + gfx::RenderText* GetRenderText() const; + + void CreateTouchSelectionControllerAndNotifyIt(); + + void ResetTouchSelectionController(); + + TextfieldModel* model() const { return textfield_->model_.get(); } + + ui::MenuModel* context_menu_contents() const { + return textfield_->context_menu_contents_.get(); + } + + ui::TouchSelectionController* touch_selection_controller() const { + return textfield_->touch_selection_controller_.get(); + } + + private: + Textfield* textfield_; + + DISALLOW_COPY_AND_ASSIGN(TextfieldTestApi); +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_TEST_API_H_ diff --git a/chromium/ui/views/controls/textfield/native_textfield_views_unittest.cc b/chromium/ui/views/controls/textfield/textfield_unittest.cc index 33196ec3159..8a8f9ace8f5 100644 --- a/chromium/ui/views/controls/textfield/native_textfield_views_unittest.cc +++ b/chromium/ui/views/controls/textfield/textfield_unittest.cc @@ -1,41 +1,35 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2014 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/controls/textfield/native_textfield_views.h" +#include "ui/views/controls/textfield/textfield.h" #include <set> #include <string> #include <vector> -#include "base/auto_reset.h" -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/callback.h" #include "base/command_line.h" -#include "base/message_loop/message_loop.h" #include "base/pickle.h" #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" #include "grit/ui_strings.h" -#include "testing/gtest/include/gtest/gtest.h" #include "ui/base/clipboard/clipboard.h" #include "ui/base/clipboard/scoped_clipboard_writer.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/ime/text_input_client.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/ui_base_switches.h" +#include "ui/base/ui_base_switches_util.h" #include "ui/events/event.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/render_text.h" -#include "ui/views/controls/textfield/textfield.h" #include "ui/views/controls/textfield/textfield_controller.h" -#include "ui/views/controls/textfield/textfield_views_model.h" +#include "ui/views/controls/textfield/textfield_model.h" +#include "ui/views/controls/textfield/textfield_test_api.h" #include "ui/views/focus/focus_manager.h" #include "ui/views/ime/mock_input_method.h" #include "ui/views/test/test_views_delegate.h" #include "ui/views/test/views_test_base.h" -#include "ui/views/widget/native_widget_private.h" #include "ui/views/widget/widget.h" #include "url/gurl.h" @@ -43,20 +37,24 @@ #include "base/win/windows_version.h" #endif +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) +#include "ui/events/linux/text_edit_key_bindings_delegate_auralinux.h" +#endif + +using base::ASCIIToUTF16; +using base::UTF8ToUTF16; +using base::WideToUTF16; + #define EXPECT_STR_EQ(ascii, utf16) EXPECT_EQ(ASCIIToUTF16(ascii), utf16) namespace { -const char16 kHebrewLetterSamekh = 0x05E1; +const base::char16 kHebrewLetterSamekh = 0x05E1; // A Textfield wrapper to intercept OnKey[Pressed|Released]() ressults. class TestTextfield : public views::Textfield { public: - explicit TestTextfield(StyleFlags style) - : Textfield(style), - key_handled_(false), - key_received_(false) { - } + TestTextfield() : Textfield(), key_handled_(false), key_received_(false) {} virtual bool OnKeyPressed(const ui::KeyEvent& e) OVERRIDE { key_received_ = true; @@ -82,20 +80,6 @@ class TestTextfield : public views::Textfield { DISALLOW_COPY_AND_ASSIGN(TestTextfield); }; -// A helper class for use with ui::TextInputClient::GetTextFromRange(). -class GetTextHelper { - public: - GetTextHelper() {} - - void set_text(const string16& text) { text_ = text; } - const string16& text() const { return text_; } - - private: - string16 text_; - - DISALLOW_COPY_AND_ASSIGN(GetTextHelper); -}; - // Convenience to make constructing a GestureEvent simpler. class GestureEventForTest : public ui::GestureEvent { public: @@ -109,24 +93,31 @@ class GestureEventForTest : public ui::GestureEvent { DISALLOW_COPY_AND_ASSIGN(GestureEventForTest); }; +base::string16 GetClipboardText(ui::ClipboardType type) { + base::string16 text; + ui::Clipboard::GetForCurrentThread()->ReadText(type, &text); + return text; +} + +void SetClipboardText(ui::ClipboardType type, const std::string& text) { + ui::ScopedClipboardWriter(ui::Clipboard::GetForCurrentThread(), type) + .WriteText(ASCIIToUTF16(text)); +} + } // namespace namespace views { -// TODO(oshima): Move tests that are independent of TextfieldViews to -// textfield_unittests.cc once we move the test utility functions -// from chrome/browser/automation/ to ui/base/test/. -class NativeTextfieldViewsTest : public ViewsTestBase, - public TextfieldController { +class TextfieldTest : public ViewsTestBase, public TextfieldController { public: - NativeTextfieldViewsTest() + TextfieldTest() : widget_(NULL), textfield_(NULL), - textfield_view_(NULL), model_(NULL), input_method_(NULL), on_before_user_action_(0), - on_after_user_action_(0) { + on_after_user_action_(0), + copied_to_clipboard_(ui::CLIPBOARD_TYPE_LAST) { } // ::testing::Test: @@ -140,21 +131,21 @@ class NativeTextfieldViewsTest : public ViewsTestBase, ViewsTestBase::TearDown(); } + ui::ClipboardType GetAndResetCopiedToClipboard() { + ui::ClipboardType clipboard_type = copied_to_clipboard_; + copied_to_clipboard_ = ui::CLIPBOARD_TYPE_LAST; + return clipboard_type; + } + // TextfieldController: virtual void ContentsChanged(Textfield* sender, - const string16& new_contents) OVERRIDE { + const base::string16& new_contents) OVERRIDE { // Paste calls TextfieldController::ContentsChanged() explicitly even if the // paste action did not change the content. So |new_contents| may match // |last_contents_|. For more info, see http://crbug.com/79002 last_contents_ = new_contents; } - virtual bool HandleKeyEvent(Textfield* sender, - const ui::KeyEvent& key_event) OVERRIDE { - // TODO(oshima): figure out how to test the keystroke. - return false; - } - virtual void OnBeforeUserAction(Textfield* sender) OVERRIDE { ++on_before_user_action_; } @@ -163,14 +154,18 @@ class NativeTextfieldViewsTest : public ViewsTestBase, ++on_after_user_action_; } - void InitTextfield(Textfield::StyleFlags style) { - InitTextfields(style, 1); + virtual void OnAfterCutOrCopy(ui::ClipboardType clipboard_type) OVERRIDE { + copied_to_clipboard_ = clipboard_type; } - void InitTextfields(Textfield::StyleFlags style, int count) { + void InitTextfield() { + InitTextfields(1); + } + + void InitTextfields(int count) { ASSERT_FALSE(textfield_); - textfield_ = new TestTextfield(style); - textfield_->SetController(this); + textfield_ = new TestTextfield(); + textfield_->set_controller(this); widget_ = new Widget(); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); params.bounds = gfx::Rect(100, 100, 100, 100); @@ -178,20 +173,17 @@ class NativeTextfieldViewsTest : public ViewsTestBase, View* container = new View(); widget_->SetContentsView(container); container->AddChildView(textfield_); - - textfield_view_ = static_cast<NativeTextfieldViews*>( - textfield_->GetNativeWrapperForTesting()); - DCHECK(textfield_view_); - textfield_view_->SetBoundsRect(params.bounds); + textfield_->SetBoundsRect(params.bounds); textfield_->set_id(1); + test_api_.reset(new TextfieldTestApi(textfield_)); for (int i = 1; i < count; i++) { - Textfield* textfield = new Textfield(style); + Textfield* textfield = new Textfield(); container->AddChildView(textfield); textfield->set_id(i + 1); } - model_ = textfield_view_->model_.get(); + model_ = test_api_->model(); model_->ClearEditHistory(); input_method_ = new MockInputMethod(); @@ -203,12 +195,8 @@ class NativeTextfieldViewsTest : public ViewsTestBase, } ui::MenuModel* GetContextMenuModel() { - textfield_view_->UpdateContextMenu(); - return textfield_view_->context_menu_contents_.get(); - } - - ui::TouchSelectionController* GetTouchSelectionController() { - return textfield_view_->touch_selection_controller_.get(); + test_api_->UpdateContextMenu(); + return test_api_->context_menu_contents(); } protected: @@ -233,7 +221,7 @@ class NativeTextfieldViewsTest : public ViewsTestBase, SendKeyEvent(key_code, false, false); } - void SendKeyEvent(char16 ch) { + void SendKeyEvent(base::char16 ch) { if (ch < 0x80) { ui::KeyboardCode code = ch == ' ' ? ui::VKEY_SPACE : @@ -246,73 +234,51 @@ class NativeTextfieldViewsTest : public ViewsTestBase, } } - string16 GetClipboardText() const { - string16 text; - ui::Clipboard::GetForCurrentThread()-> - ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &text); - return text; - } - - void SetClipboardText(const std::string& text) { - ui::ScopedClipboardWriter clipboard_writer( - ui::Clipboard::GetForCurrentThread(), - ui::CLIPBOARD_TYPE_COPY_PASTE); - clipboard_writer.WriteText(ASCIIToUTF16(text)); - } - View* GetFocusedView() { return widget_->GetFocusManager()->GetFocusedView(); } int GetCursorPositionX(int cursor_pos) { - gfx::RenderText* render_text = textfield_view_->GetRenderText(); - return render_text->GetCursorBounds( + return test_api_->GetRenderText()->GetCursorBounds( gfx::SelectionModel(cursor_pos, gfx::CURSOR_FORWARD), false).x(); } // Get the current cursor bounds. gfx::Rect GetCursorBounds() { - gfx::RenderText* render_text = textfield_view_->GetRenderText(); - gfx::Rect bounds = render_text->GetUpdatedCursorBounds(); - return bounds; + return test_api_->GetRenderText()->GetUpdatedCursorBounds(); } // Get the cursor bounds of |sel|. gfx::Rect GetCursorBounds(const gfx::SelectionModel& sel) { - gfx::RenderText* render_text = textfield_view_->GetRenderText(); - gfx::Rect bounds = render_text->GetCursorBounds(sel, true); - return bounds; + return test_api_->GetRenderText()->GetCursorBounds(sel, true); } gfx::Rect GetDisplayRect() { - return textfield_view_->GetRenderText()->display_rect(); + return test_api_->GetRenderText()->display_rect(); } // Mouse click on the point whose x-axis is |bound|'s x plus |x_offset| and // y-axis is in the middle of |bound|'s vertical range. void MouseClick(const gfx::Rect bound, int x_offset) { - gfx::Point point(bound.x() + x_offset, bound.y() + bound.height() / 2); + gfx::Point point(bound.x() + x_offset, bound.y() + bound.height() / 2); ui::MouseEvent click(ui::ET_MOUSE_PRESSED, point, point, - ui::EF_LEFT_MOUSE_BUTTON); - textfield_view_->OnMousePressed(click); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMousePressed(click); ui::MouseEvent release(ui::ET_MOUSE_RELEASED, point, point, - ui::EF_LEFT_MOUSE_BUTTON); - textfield_view_->OnMouseReleased(release); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMouseReleased(release); } // This is to avoid double/triple click. void NonClientMouseClick() { ui::MouseEvent click(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), - ui::EF_LEFT_MOUSE_BUTTON | ui::EF_IS_NON_CLIENT); - textfield_view_->OnMousePressed(click); + ui::EF_LEFT_MOUSE_BUTTON | ui::EF_IS_NON_CLIENT, + ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMousePressed(click); ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), - ui::EF_LEFT_MOUSE_BUTTON | ui::EF_IS_NON_CLIENT); - textfield_view_->OnMouseReleased(release); - } - - // Wrap for visibility in test classes. - ui::TextInputType GetTextInputType() { - return textfield_view_->GetTextInputType(); + ui::EF_LEFT_MOUSE_BUTTON | ui::EF_IS_NON_CLIENT, + ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMouseReleased(release); } void VerifyTextfieldContextMenuContents(bool textfield_has_selection, @@ -322,7 +288,8 @@ class NativeTextfieldViewsTest : public ViewsTestBase, EXPECT_TRUE(menu->IsEnabledAt(1 /* Separator */)); EXPECT_EQ(textfield_has_selection, menu->IsEnabledAt(2 /* CUT */)); EXPECT_EQ(textfield_has_selection, menu->IsEnabledAt(3 /* COPY */)); - EXPECT_NE(GetClipboardText().empty(), menu->IsEnabledAt(4 /* PASTE */)); + EXPECT_NE(GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE).empty(), + menu->IsEnabledAt(4 /* PASTE */)); EXPECT_EQ(textfield_has_selection, menu->IsEnabledAt(5 /* DELETE */)); EXPECT_TRUE(menu->IsEnabledAt(6 /* Separator */)); EXPECT_TRUE(menu->IsEnabledAt(7 /* SELECT ALL */)); @@ -332,11 +299,11 @@ class NativeTextfieldViewsTest : public ViewsTestBase, Widget* widget_; TestTextfield* textfield_; - NativeTextfieldViews* textfield_view_; - TextfieldViewsModel* model_; + scoped_ptr<TextfieldTestApi> test_api_; + TextfieldModel* model_; // The string from Controller::ContentsChanged callback. - string16 last_contents_; + base::string16 last_contents_; // For testing input method related behaviors. MockInputMethod* input_method_; @@ -348,112 +315,36 @@ class NativeTextfieldViewsTest : public ViewsTestBase, int on_after_user_action_; private: - DISALLOW_COPY_AND_ASSIGN(NativeTextfieldViewsTest); + ui::ClipboardType copied_to_clipboard_; + + DISALLOW_COPY_AND_ASSIGN(TextfieldTest); }; -TEST_F(NativeTextfieldViewsTest, ModelChangesTest) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, ModelChangesTest) { + InitTextfield(); // TextfieldController::ContentsChanged() shouldn't be called when changing // text programmatically. last_contents_.clear(); textfield_->SetText(ASCIIToUTF16("this is")); - EXPECT_STR_EQ("this is", model_->GetText()); + EXPECT_STR_EQ("this is", model_->text()); EXPECT_STR_EQ("this is", textfield_->text()); EXPECT_TRUE(last_contents_.empty()); textfield_->AppendText(ASCIIToUTF16(" a test")); - EXPECT_STR_EQ("this is a test", model_->GetText()); + EXPECT_STR_EQ("this is a test", model_->text()); EXPECT_STR_EQ("this is a test", textfield_->text()); EXPECT_TRUE(last_contents_.empty()); - EXPECT_EQ(string16(), textfield_->GetSelectedText()); + EXPECT_EQ(base::string16(), textfield_->GetSelectedText()); textfield_->SelectAll(false); EXPECT_STR_EQ("this is a test", textfield_->GetSelectedText()); EXPECT_TRUE(last_contents_.empty()); } -TEST_F(NativeTextfieldViewsTest, ModelChangesTestLowerCase) { - // Check if |model_|'s text is properly lowercased for STYLE_LOWERCASE. - InitTextfield(Textfield::STYLE_LOWERCASE); - EXPECT_EQ(0U, textfield_->GetCursorPosition()); - - last_contents_.clear(); - textfield_->SetText(ASCIIToUTF16("THIS IS")); - EXPECT_EQ(7U, textfield_->GetCursorPosition()); - - EXPECT_STR_EQ("this is", model_->GetText()); - EXPECT_STR_EQ("THIS IS", textfield_->text()); - EXPECT_TRUE(last_contents_.empty()); - - textfield_->AppendText(ASCIIToUTF16(" A TEST")); - EXPECT_EQ(7U, textfield_->GetCursorPosition()); - EXPECT_STR_EQ("this is a test", model_->GetText()); - EXPECT_STR_EQ("THIS IS A TEST", textfield_->text()); - - EXPECT_TRUE(last_contents_.empty()); -} - -TEST_F(NativeTextfieldViewsTest, ModelChangesTestLowerCaseI18n) { - // Check if lower case conversion works for non-ASCII characters. - InitTextfield(Textfield::STYLE_LOWERCASE); - EXPECT_EQ(0U, textfield_->GetCursorPosition()); - - last_contents_.clear(); - // Zenkaku Japanese "ABCabc" - textfield_->SetText(WideToUTF16(L"\xFF21\xFF22\xFF23\xFF41\xFF42\xFF43")); - EXPECT_EQ(6U, textfield_->GetCursorPosition()); - // Zenkaku Japanese "abcabc" - EXPECT_EQ(WideToUTF16(L"\xFF41\xFF42\xFF43\xFF41\xFF42\xFF43"), - model_->GetText()); - // Zenkaku Japanese "ABCabc" - EXPECT_EQ(WideToUTF16(L"\xFF21\xFF22\xFF23\xFF41\xFF42\xFF43"), - textfield_->text()); - EXPECT_TRUE(last_contents_.empty()); - - // Zenkaku Japanese "XYZxyz" - textfield_->AppendText(WideToUTF16(L"\xFF38\xFF39\xFF3A\xFF58\xFF59\xFF5A")); - EXPECT_EQ(6U, textfield_->GetCursorPosition()); - // Zenkaku Japanese "abcabcxyzxyz" - EXPECT_EQ(WideToUTF16(L"\xFF41\xFF42\xFF43\xFF41\xFF42\xFF43" - L"\xFF58\xFF59\xFF5A\xFF58\xFF59\xFF5A"), - model_->GetText()); - // Zenkaku Japanese "ABCabcXYZxyz" - EXPECT_EQ(WideToUTF16(L"\xFF21\xFF22\xFF23\xFF41\xFF42\xFF43" - L"\xFF38\xFF39\xFF3A\xFF58\xFF59\xFF5A"), - textfield_->text()); - EXPECT_TRUE(last_contents_.empty()); -} - -TEST_F(NativeTextfieldViewsTest, ModelChangesTestLowerCaseWithLocale) { - // Check if lower case conversion honors locale properly. - std::string locale = l10n_util::GetApplicationLocale(""); - base::i18n::SetICUDefaultLocale("tr"); - - InitTextfield(Textfield::STYLE_LOWERCASE); - EXPECT_EQ(0U, textfield_->GetCursorPosition()); - - last_contents_.clear(); - // Turkish 'I' should be converted to dotless 'i' (U+0131). - textfield_->SetText(WideToUTF16(L"I")); - EXPECT_EQ(1U, textfield_->GetCursorPosition()); - EXPECT_EQ(WideToUTF16(L"\x0131"), model_->GetText()); - EXPECT_EQ(WideToUTF16(L"I"), textfield_->text()); - EXPECT_TRUE(last_contents_.empty()); - - base::i18n::SetICUDefaultLocale(locale); - - // On default (en) locale, 'I' should be converted to 'i'. - textfield_->SetText(WideToUTF16(L"I")); - EXPECT_EQ(1U, textfield_->GetCursorPosition()); - EXPECT_EQ(WideToUTF16(L"i"), model_->GetText()); - EXPECT_EQ(WideToUTF16(L"I"), textfield_->text()); - EXPECT_TRUE(last_contents_.empty()); -} - -TEST_F(NativeTextfieldViewsTest, KeyTest) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, KeyTest) { + InitTextfield(); // Event flags: key, alt, shift, ctrl, caps-lock. SendKeyEvent(ui::VKEY_T, false, true, false, false); SendKeyEvent(ui::VKEY_E, false, false, false, false); @@ -466,9 +357,9 @@ TEST_F(NativeTextfieldViewsTest, KeyTest) { EXPECT_STR_EQ("TexT!1!1", textfield_->text()); } -TEST_F(NativeTextfieldViewsTest, ControlAndSelectTest) { +TEST_F(TextfieldTest, ControlAndSelectTest) { // Insert a test string in a textfield. - InitTextfield(Textfield::STYLE_DEFAULT); + InitTextfield(); textfield_->SetText(ASCIIToUTF16("one two three")); SendKeyEvent(ui::VKEY_HOME, false /* shift */, false /* control */); SendKeyEvent(ui::VKEY_RIGHT, true, false); @@ -501,9 +392,9 @@ TEST_F(NativeTextfieldViewsTest, ControlAndSelectTest) { EXPECT_STR_EQ("ZERO ", textfield_->GetSelectedText()); } -TEST_F(NativeTextfieldViewsTest, InsertionDeletionTest) { +TEST_F(TextfieldTest, InsertionDeletionTest) { // Insert a test string in a textfield. - InitTextfield(Textfield::STYLE_DEFAULT); + InitTextfield(); for (size_t i = 0; i < 10; i++) SendKeyEvent(static_cast<ui::KeyboardCode>(ui::VKEY_A + i)); EXPECT_STR_EQ("abcdefghij", textfield_->text()); @@ -528,13 +419,13 @@ TEST_F(NativeTextfieldViewsTest, InsertionDeletionTest) { SendKeyEvent(ui::VKEY_BACK, false, false, true, false); EXPECT_STR_EQ("one two three ", textfield_->text()); - // Delete text preceeding the cursor in chromeos, do nothing in windows. + // Delete to a line break on Linux and ChromeOS, to a word break on Windows. SendKeyEvent(ui::VKEY_LEFT, false, false, true, false); SendKeyEvent(ui::VKEY_BACK, false, true, true, false); -#if defined(OS_WIN) - EXPECT_STR_EQ("one two three ", textfield_->text()); -#else +#if defined(OS_LINUX) EXPECT_STR_EQ("three ", textfield_->text()); +#else + EXPECT_STR_EQ("one three ", textfield_->text()); #endif // Delete the next word from cursor. @@ -543,21 +434,22 @@ TEST_F(NativeTextfieldViewsTest, InsertionDeletionTest) { SendKeyEvent(ui::VKEY_DELETE, false, false, true, false); EXPECT_STR_EQ(" two three four", textfield_->text()); - // Delete text following the cursor in chromeos, do nothing in windows. + // Delete to a line break on Linux and ChromeOS, to a word break on Windows. SendKeyEvent(ui::VKEY_RIGHT, false, false, true, false); SendKeyEvent(ui::VKEY_DELETE, false, true, true, false); -#if defined(OS_WIN) - EXPECT_STR_EQ(" two three four", textfield_->text()); -#else +#if defined(OS_LINUX) EXPECT_STR_EQ(" two", textfield_->text()); +#else + EXPECT_STR_EQ(" two four", textfield_->text()); #endif } -TEST_F(NativeTextfieldViewsTest, PasswordTest) { - InitTextfield(Textfield::STYLE_OBSCURED); - EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, GetTextInputType()); +TEST_F(TextfieldTest, PasswordTest) { + InitTextfield(); + textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, textfield_->GetTextInputType()); EXPECT_TRUE(textfield_->enabled()); - EXPECT_TRUE(textfield_->focusable()); + EXPECT_TRUE(textfield_->IsFocusable()); last_contents_.clear(); textfield_->SetText(ASCIIToUTF16("password")); @@ -565,143 +457,154 @@ TEST_F(NativeTextfieldViewsTest, PasswordTest) { EXPECT_STR_EQ("password", textfield_->text()); EXPECT_TRUE(last_contents_.empty()); model_->SelectAll(false); - SetClipboardText("foo"); + SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "foo"); // Cut and copy should be disabled. - EXPECT_FALSE(textfield_view_->IsCommandIdEnabled(IDS_APP_CUT)); - textfield_view_->ExecuteCommand(IDS_APP_CUT, 0); + EXPECT_FALSE(textfield_->IsCommandIdEnabled(IDS_APP_CUT)); + textfield_->ExecuteCommand(IDS_APP_CUT, 0); SendKeyEvent(ui::VKEY_X, false, true); - EXPECT_FALSE(textfield_view_->IsCommandIdEnabled(IDS_APP_COPY)); - textfield_view_->ExecuteCommand(IDS_APP_COPY, 0); + EXPECT_FALSE(textfield_->IsCommandIdEnabled(IDS_APP_COPY)); + textfield_->ExecuteCommand(IDS_APP_COPY, 0); SendKeyEvent(ui::VKEY_C, false, true); SendKeyEvent(ui::VKEY_INSERT, false, true); - EXPECT_STR_EQ("foo", string16(GetClipboardText())); + EXPECT_STR_EQ("foo", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); EXPECT_STR_EQ("password", textfield_->text()); // [Shift]+[Delete] should just delete without copying text to the clipboard. textfield_->SelectAll(false); SendKeyEvent(ui::VKEY_DELETE, true, false); // Paste should work normally. - EXPECT_TRUE(textfield_view_->IsCommandIdEnabled(IDS_APP_PASTE)); - textfield_view_->ExecuteCommand(IDS_APP_PASTE, 0); + EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_PASTE)); + textfield_->ExecuteCommand(IDS_APP_PASTE, 0); SendKeyEvent(ui::VKEY_V, false, true); SendKeyEvent(ui::VKEY_INSERT, true, false); - EXPECT_STR_EQ("foo", string16(GetClipboardText())); + EXPECT_STR_EQ("foo", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); EXPECT_STR_EQ("foofoofoo", textfield_->text()); } -TEST_F(NativeTextfieldViewsTest, InputTypeSetsObscured) { - InitTextfield(Textfield::STYLE_DEFAULT); - - // Defaults to TEXT - EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, GetTextInputType()); - - // Setting to TEXT_INPUT_TYPE_PASSWORD also sets obscured state of textfield. - textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); - EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, GetTextInputType()); - EXPECT_TRUE(textfield_->IsObscured()); -} - -TEST_F(NativeTextfieldViewsTest, ObscuredSetsInputType) { - InitTextfield(Textfield::STYLE_DEFAULT); - - // Defaults to TEXT - EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, GetTextInputType()); - - textfield_->SetObscured(true); - EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, GetTextInputType()); - - textfield_->SetObscured(false); - EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, GetTextInputType()); -} - -TEST_F(NativeTextfieldViewsTest, TextInputType) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, TextInputType) { + InitTextfield(); // Defaults to TEXT - EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, GetTextInputType()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, textfield_->GetTextInputType()); // And can be set. textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_URL); - EXPECT_EQ(ui::TEXT_INPUT_TYPE_URL, GetTextInputType()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_URL, textfield_->GetTextInputType()); + textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, textfield_->GetTextInputType()); // Readonly textfields have type NONE textfield_->SetReadOnly(true); - EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, GetTextInputType()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, textfield_->GetTextInputType()); textfield_->SetReadOnly(false); - EXPECT_EQ(ui::TEXT_INPUT_TYPE_URL, GetTextInputType()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, textfield_->GetTextInputType()); // As do disabled textfields textfield_->SetEnabled(false); - EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, GetTextInputType()); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, textfield_->GetTextInputType()); + + textfield_->SetEnabled(true); + EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, textfield_->GetTextInputType()); } -TEST_F(NativeTextfieldViewsTest, OnKeyPressReturnValueTest) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, OnKeyPress) { + InitTextfield(); - // Character keys will be handled by input method. + // Character keys are handled by the input method. SendKeyEvent(ui::VKEY_A); EXPECT_TRUE(textfield_->key_received()); EXPECT_FALSE(textfield_->key_handled()); textfield_->clear(); - // Home will be handled. - SendKeyEvent(ui::VKEY_HOME); + // Arrow keys and home/end are handled by the textfield. + SendKeyEvent(ui::VKEY_LEFT); EXPECT_TRUE(textfield_->key_received()); EXPECT_TRUE(textfield_->key_handled()); textfield_->clear(); - // F24, up/down key won't be handled. - SendKeyEvent(ui::VKEY_F24); + SendKeyEvent(ui::VKEY_RIGHT); EXPECT_TRUE(textfield_->key_received()); - EXPECT_FALSE(textfield_->key_handled()); + EXPECT_TRUE(textfield_->key_handled()); textfield_->clear(); - SendKeyEvent(ui::VKEY_UP); + SendKeyEvent(ui::VKEY_HOME); EXPECT_TRUE(textfield_->key_received()); - EXPECT_FALSE(textfield_->key_handled()); + EXPECT_TRUE(textfield_->key_handled()); textfield_->clear(); - SendKeyEvent(ui::VKEY_DOWN); + SendKeyEvent(ui::VKEY_END); EXPECT_TRUE(textfield_->key_received()); - EXPECT_FALSE(textfield_->key_handled()); + EXPECT_TRUE(textfield_->key_handled()); textfield_->clear(); - // Empty Textfield does not handle left/right. - textfield_->SetText(string16()); - SendKeyEvent(ui::VKEY_LEFT); + // F24, up/down key won't be handled. + SendKeyEvent(ui::VKEY_F24); EXPECT_TRUE(textfield_->key_received()); EXPECT_FALSE(textfield_->key_handled()); textfield_->clear(); - SendKeyEvent(ui::VKEY_RIGHT); + SendKeyEvent(ui::VKEY_UP); EXPECT_TRUE(textfield_->key_received()); EXPECT_FALSE(textfield_->key_handled()); textfield_->clear(); - // Add a char. Right key should not be handled when cursor is at the end. - SendKeyEvent(ui::VKEY_B); - SendKeyEvent(ui::VKEY_RIGHT); + SendKeyEvent(ui::VKEY_DOWN); EXPECT_TRUE(textfield_->key_received()); EXPECT_FALSE(textfield_->key_handled()); textfield_->clear(); +} - // First left key is handled to move cursor left to the beginning. - SendKeyEvent(ui::VKEY_LEFT); +// Tests that default key bindings are handled even with a delegate installed. +TEST_F(TextfieldTest, OnKeyPressBinding) { + InitTextfield(); + +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + // Install a TextEditKeyBindingsDelegateAuraLinux that does nothing. + class TestDelegate : public ui::TextEditKeyBindingsDelegateAuraLinux { + public: + TestDelegate() {} + virtual ~TestDelegate() {} + + virtual bool MatchEvent( + const ui::Event& event, + std::vector<ui::TextEditCommandAuraLinux>* commands) OVERRIDE { + return false; + } + + private: + DISALLOW_COPY_AND_ASSIGN(TestDelegate); + }; + + TestDelegate delegate; + ui::SetTextEditKeyBindingsDelegate(&delegate); +#endif + + SendKeyEvent(ui::VKEY_A, false, false); + EXPECT_STR_EQ("a", textfield_->text()); + textfield_->clear(); + + // Undo/Redo command keys are handled by the textfield. + SendKeyEvent(ui::VKEY_Z, false, true); EXPECT_TRUE(textfield_->key_received()); EXPECT_TRUE(textfield_->key_handled()); + EXPECT_TRUE(textfield_->text().empty()); textfield_->clear(); - // Now left key should not be handled. - SendKeyEvent(ui::VKEY_LEFT); + SendKeyEvent(ui::VKEY_Z, true, true); EXPECT_TRUE(textfield_->key_received()); - EXPECT_FALSE(textfield_->key_handled()); + EXPECT_TRUE(textfield_->key_handled()); + EXPECT_STR_EQ("a", textfield_->text()); textfield_->clear(); + +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + ui::SetTextEditKeyBindingsDelegate(NULL); +#endif } -TEST_F(NativeTextfieldViewsTest, CursorMovement) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, CursorMovement) { + InitTextfield(); // Test with trailing whitespace. textfield_->SetText(ASCIIToUTF16("one two hre ")); @@ -750,8 +653,8 @@ TEST_F(NativeTextfieldViewsTest, CursorMovement) { EXPECT_STR_EQ("one two", last_contents_); } -TEST_F(NativeTextfieldViewsTest, FocusTraversalTest) { - InitTextfields(Textfield::STYLE_DEFAULT, 3); +TEST_F(TextfieldTest, FocusTraversalTest) { + InitTextfields(3); textfield_->RequestFocus(); EXPECT_EQ(1, GetFocusedView()->id()); @@ -777,21 +680,21 @@ TEST_F(NativeTextfieldViewsTest, FocusTraversalTest) { textfield_->RequestFocus(); EXPECT_EQ(1, GetFocusedView()->id()); - // Test if clicking on textfield view sets the focus to textfield_. + // Test if clicking on textfield view sets the focus. widget_->GetFocusManager()->AdvanceFocus(true); EXPECT_EQ(3, GetFocusedView()->id()); ui::MouseEvent click(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), - ui::EF_LEFT_MOUSE_BUTTON); - textfield_view_->OnMousePressed(click); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMousePressed(click); EXPECT_EQ(1, GetFocusedView()->id()); } -TEST_F(NativeTextfieldViewsTest, ContextMenuDisplayTest) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, ContextMenuDisplayTest) { + InitTextfield(); EXPECT_TRUE(textfield_->context_menu_controller()); textfield_->SetText(ASCIIToUTF16("hello world")); ui::Clipboard::GetForCurrentThread()->Clear(ui::CLIPBOARD_TYPE_COPY_PASTE); - textfield_view_->ClearEditHistory(); + textfield_->ClearEditHistory(); EXPECT_TRUE(GetContextMenuModel()); VerifyTextfieldContextMenuContents(false, false, GetContextMenuModel()); @@ -805,183 +708,183 @@ TEST_F(NativeTextfieldViewsTest, ContextMenuDisplayTest) { VerifyTextfieldContextMenuContents(true, true, GetContextMenuModel()); // Exercise the "paste enabled?" check in the verifier. - SetClipboardText("Test"); + SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "Test"); VerifyTextfieldContextMenuContents(true, true, GetContextMenuModel()); } -TEST_F(NativeTextfieldViewsTest, DoubleAndTripleClickTest) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, DoubleAndTripleClickTest) { + InitTextfield(); textfield_->SetText(ASCIIToUTF16("hello world")); ui::MouseEvent click(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), - ui::EF_LEFT_MOUSE_BUTTON); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), - ui::EF_LEFT_MOUSE_BUTTON); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); ui::MouseEvent double_click( ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), - ui::EF_LEFT_MOUSE_BUTTON | ui::EF_IS_DOUBLE_CLICK); + ui::EF_LEFT_MOUSE_BUTTON | ui::EF_IS_DOUBLE_CLICK, + ui::EF_LEFT_MOUSE_BUTTON); // Test for double click. - textfield_view_->OnMousePressed(click); - textfield_view_->OnMouseReleased(release); + textfield_->OnMousePressed(click); + textfield_->OnMouseReleased(release); EXPECT_TRUE(textfield_->GetSelectedText().empty()); - textfield_view_->OnMousePressed(double_click); - textfield_view_->OnMouseReleased(release); + textfield_->OnMousePressed(double_click); + textfield_->OnMouseReleased(release); EXPECT_STR_EQ("hello", textfield_->GetSelectedText()); // Test for triple click. - textfield_view_->OnMousePressed(click); - textfield_view_->OnMouseReleased(release); + textfield_->OnMousePressed(click); + textfield_->OnMouseReleased(release); EXPECT_STR_EQ("hello world", textfield_->GetSelectedText()); // Another click should reset back to double click. - textfield_view_->OnMousePressed(click); - textfield_view_->OnMouseReleased(release); + textfield_->OnMousePressed(click); + textfield_->OnMouseReleased(release); EXPECT_STR_EQ("hello", textfield_->GetSelectedText()); } -TEST_F(NativeTextfieldViewsTest, DragToSelect) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, DragToSelect) { + InitTextfield(); textfield_->SetText(ASCIIToUTF16("hello world")); const int kStart = GetCursorPositionX(5); const int kEnd = 500; gfx::Point start_point(kStart, 0); gfx::Point end_point(kEnd, 0); ui::MouseEvent click_a(ui::ET_MOUSE_PRESSED, start_point, start_point, - ui::EF_LEFT_MOUSE_BUTTON); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); ui::MouseEvent click_b(ui::ET_MOUSE_PRESSED, end_point, end_point, - ui::EF_LEFT_MOUSE_BUTTON); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); ui::MouseEvent drag_left(ui::ET_MOUSE_DRAGGED, gfx::Point(), gfx::Point(), - ui::EF_LEFT_MOUSE_BUTTON); + ui::EF_LEFT_MOUSE_BUTTON, 0); ui::MouseEvent drag_right(ui::ET_MOUSE_DRAGGED, end_point, end_point, - ui::EF_LEFT_MOUSE_BUTTON); + ui::EF_LEFT_MOUSE_BUTTON, 0); ui::MouseEvent release(ui::ET_MOUSE_RELEASED, end_point, end_point, - ui::EF_LEFT_MOUSE_BUTTON); - textfield_view_->OnMousePressed(click_a); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMousePressed(click_a); EXPECT_TRUE(textfield_->GetSelectedText().empty()); // Check that dragging left selects the beginning of the string. - textfield_view_->OnMouseDragged(drag_left); - string16 text_left = textfield_->GetSelectedText(); + textfield_->OnMouseDragged(drag_left); + base::string16 text_left = textfield_->GetSelectedText(); EXPECT_STR_EQ("hello", text_left); // Check that dragging right selects the rest of the string. - textfield_view_->OnMouseDragged(drag_right); - string16 text_right = textfield_->GetSelectedText(); + textfield_->OnMouseDragged(drag_right); + base::string16 text_right = textfield_->GetSelectedText(); EXPECT_STR_EQ(" world", text_right); // Check that releasing in the same location does not alter the selection. - textfield_view_->OnMouseReleased(release); + textfield_->OnMouseReleased(release); EXPECT_EQ(text_right, textfield_->GetSelectedText()); // Check that dragging from beyond the text length works too. - textfield_view_->OnMousePressed(click_b); - textfield_view_->OnMouseDragged(drag_left); - textfield_view_->OnMouseReleased(release); + textfield_->OnMousePressed(click_b); + textfield_->OnMouseDragged(drag_left); + textfield_->OnMouseReleased(release); EXPECT_EQ(textfield_->text(), textfield_->GetSelectedText()); } #if defined(OS_WIN) -TEST_F(NativeTextfieldViewsTest, DragAndDrop_AcceptDrop) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, DragAndDrop_AcceptDrop) { + InitTextfield(); textfield_->SetText(ASCIIToUTF16("hello world")); ui::OSExchangeData data; - string16 string(ASCIIToUTF16("string ")); + base::string16 string(ASCIIToUTF16("string ")); data.SetString(string); int formats = 0; std::set<OSExchangeData::CustomFormat> custom_formats; // Ensure that disabled textfields do not accept drops. textfield_->SetEnabled(false); - EXPECT_FALSE(textfield_view_->GetDropFormats(&formats, &custom_formats)); + EXPECT_FALSE(textfield_->GetDropFormats(&formats, &custom_formats)); EXPECT_EQ(0, formats); EXPECT_TRUE(custom_formats.empty()); - EXPECT_FALSE(textfield_view_->CanDrop(data)); + EXPECT_FALSE(textfield_->CanDrop(data)); textfield_->SetEnabled(true); // Ensure that read-only textfields do not accept drops. textfield_->SetReadOnly(true); - EXPECT_FALSE(textfield_view_->GetDropFormats(&formats, &custom_formats)); + EXPECT_FALSE(textfield_->GetDropFormats(&formats, &custom_formats)); EXPECT_EQ(0, formats); EXPECT_TRUE(custom_formats.empty()); - EXPECT_FALSE(textfield_view_->CanDrop(data)); + EXPECT_FALSE(textfield_->CanDrop(data)); textfield_->SetReadOnly(false); // Ensure that enabled and editable textfields do accept drops. - EXPECT_TRUE(textfield_view_->GetDropFormats(&formats, &custom_formats)); + EXPECT_TRUE(textfield_->GetDropFormats(&formats, &custom_formats)); EXPECT_EQ(ui::OSExchangeData::STRING, formats); EXPECT_TRUE(custom_formats.empty()); - EXPECT_TRUE(textfield_view_->CanDrop(data)); + EXPECT_TRUE(textfield_->CanDrop(data)); gfx::Point drop_point(GetCursorPositionX(6), 0); ui::DropTargetEvent drop(data, drop_point, drop_point, ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE); EXPECT_EQ(ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE, - textfield_view_->OnDragUpdated(drop)); - EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, textfield_view_->OnPerformDrop(drop)); + textfield_->OnDragUpdated(drop)); + EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, textfield_->OnPerformDrop(drop)); EXPECT_STR_EQ("hello string world", textfield_->text()); // Ensure that textfields do not accept non-OSExchangeData::STRING types. ui::OSExchangeData bad_data; bad_data.SetFilename(base::FilePath(FILE_PATH_LITERAL("x"))); -#if defined(OS_WIN) ui::OSExchangeData::CustomFormat fmt = ui::Clipboard::GetBitmapFormatType(); bad_data.SetPickledData(fmt, Pickle()); bad_data.SetFileContents(base::FilePath(L"x"), "x"); - bad_data.SetHtml(string16(ASCIIToUTF16("x")), GURL("x.org")); + bad_data.SetHtml(base::string16(ASCIIToUTF16("x")), GURL("x.org")); ui::OSExchangeData::DownloadFileInfo download(base::FilePath(), NULL); bad_data.SetDownloadFileInfo(download); -#endif - EXPECT_FALSE(textfield_view_->CanDrop(bad_data)); + EXPECT_FALSE(textfield_->CanDrop(bad_data)); } #endif -TEST_F(NativeTextfieldViewsTest, DragAndDrop_InitiateDrag) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, DragAndDrop_InitiateDrag) { + InitTextfield(); textfield_->SetText(ASCIIToUTF16("hello string world")); // Ensure the textfield will provide selected text for drag data. - string16 string; + base::string16 string; ui::OSExchangeData data; const gfx::Range kStringRange(6, 12); textfield_->SelectRange(kStringRange); const gfx::Point kStringPoint(GetCursorPositionX(9), 0); - textfield_view_->WriteDragDataForView(NULL, kStringPoint, &data); + textfield_->WriteDragDataForView(NULL, kStringPoint, &data); EXPECT_TRUE(data.GetString(&string)); EXPECT_EQ(textfield_->GetSelectedText(), string); // Ensure that disabled textfields do not support drag operations. textfield_->SetEnabled(false); EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, - textfield_view_->GetDragOperationsForView(NULL, kStringPoint)); + textfield_->GetDragOperationsForView(NULL, kStringPoint)); textfield_->SetEnabled(true); // Ensure that textfields without selections do not support drag operations. textfield_->ClearSelection(); EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, - textfield_view_->GetDragOperationsForView(NULL, kStringPoint)); + textfield_->GetDragOperationsForView(NULL, kStringPoint)); textfield_->SelectRange(kStringRange); // Ensure that password textfields do not support drag operations. - textfield_->SetObscured(true); + textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, - textfield_view_->GetDragOperationsForView(NULL, kStringPoint)); - textfield_->SetObscured(false); + textfield_->GetDragOperationsForView(NULL, kStringPoint)); + textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT); // Ensure that textfields only initiate drag operations inside the selection. ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, kStringPoint, kStringPoint, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); - textfield_view_->OnMousePressed(press_event); + textfield_->OnMousePressed(press_event); EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, - textfield_view_->GetDragOperationsForView(NULL, gfx::Point())); - EXPECT_FALSE(textfield_view_->CanStartDragForView(NULL, gfx::Point(), - gfx::Point())); + textfield_->GetDragOperationsForView(NULL, gfx::Point())); + EXPECT_FALSE(textfield_->CanStartDragForView(NULL, gfx::Point(), + gfx::Point())); EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, - textfield_view_->GetDragOperationsForView(NULL, kStringPoint)); - EXPECT_TRUE(textfield_view_->CanStartDragForView(NULL, kStringPoint, - gfx::Point())); + textfield_->GetDragOperationsForView(NULL, kStringPoint)); + EXPECT_TRUE(textfield_->CanStartDragForView(NULL, kStringPoint, + gfx::Point())); // Ensure that textfields support local moves. EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY, - textfield_view_->GetDragOperationsForView(textfield_view_, kStringPoint)); + textfield_->GetDragOperationsForView(textfield_, kStringPoint)); } -TEST_F(NativeTextfieldViewsTest, DragAndDrop_ToTheRight) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, DragAndDrop_ToTheRight) { + InitTextfield(); textfield_->SetText(ASCIIToUTF16("hello world")); - string16 string; + base::string16 string; ui::OSExchangeData data; int formats = 0; int operations = 0; @@ -991,31 +894,29 @@ TEST_F(NativeTextfieldViewsTest, DragAndDrop_ToTheRight) { textfield_->SelectRange(gfx::Range(1, 5)); gfx::Point point(GetCursorPositionX(3), 0); ui::MouseEvent click_a(ui::ET_MOUSE_PRESSED, point, point, - ui::EF_LEFT_MOUSE_BUTTON); - textfield_view_->OnMousePressed(click_a); - EXPECT_TRUE(textfield_view_->CanStartDragForView(textfield_view_, - click_a.location(), gfx::Point())); - operations = textfield_view_->GetDragOperationsForView(textfield_view_, - click_a.location()); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMousePressed(click_a); + EXPECT_TRUE(textfield_->CanStartDragForView(textfield_, click_a.location(), + gfx::Point())); + operations = textfield_->GetDragOperationsForView(textfield_, + click_a.location()); EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY, operations); - textfield_view_->WriteDragDataForView(NULL, click_a.location(), &data); + textfield_->WriteDragDataForView(NULL, click_a.location(), &data); EXPECT_TRUE(data.GetString(&string)); EXPECT_EQ(textfield_->GetSelectedText(), string); - EXPECT_TRUE(textfield_view_->GetDropFormats(&formats, &custom_formats)); + EXPECT_TRUE(textfield_->GetDropFormats(&formats, &custom_formats)); EXPECT_EQ(ui::OSExchangeData::STRING, formats); EXPECT_TRUE(custom_formats.empty()); // Drop "ello" after "w". const gfx::Point kDropPoint(GetCursorPositionX(7), 0); - EXPECT_TRUE(textfield_view_->CanDrop(data)); + EXPECT_TRUE(textfield_->CanDrop(data)); ui::DropTargetEvent drop_a(data, kDropPoint, kDropPoint, operations); - EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE, - textfield_view_->OnDragUpdated(drop_a)); - EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE, - textfield_view_->OnPerformDrop(drop_a)); + EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE, textfield_->OnDragUpdated(drop_a)); + EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE, textfield_->OnPerformDrop(drop_a)); EXPECT_STR_EQ("h welloorld", textfield_->text()); - textfield_view_->OnDragDone(); + textfield_->OnDragDone(); // Undo/Redo the drag&drop change. SendKeyEvent(ui::VKEY_Z, false, true); @@ -1032,11 +933,11 @@ TEST_F(NativeTextfieldViewsTest, DragAndDrop_ToTheRight) { EXPECT_STR_EQ("h welloorld", textfield_->text()); } -TEST_F(NativeTextfieldViewsTest, DragAndDrop_ToTheLeft) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, DragAndDrop_ToTheLeft) { + InitTextfield(); textfield_->SetText(ASCIIToUTF16("hello world")); - string16 string; + base::string16 string; ui::OSExchangeData data; int formats = 0; int operations = 0; @@ -1046,31 +947,29 @@ TEST_F(NativeTextfieldViewsTest, DragAndDrop_ToTheLeft) { textfield_->SelectRange(gfx::Range(5, 10)); gfx::Point point(GetCursorPositionX(7), 0); ui::MouseEvent click_a(ui::ET_MOUSE_PRESSED, point, point, - ui::EF_LEFT_MOUSE_BUTTON); - textfield_view_->OnMousePressed(click_a); - EXPECT_TRUE(textfield_view_->CanStartDragForView(textfield_view_, - click_a.location(), gfx::Point())); - operations = textfield_view_->GetDragOperationsForView(textfield_view_, - click_a.location()); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMousePressed(click_a); + EXPECT_TRUE(textfield_->CanStartDragForView(textfield_, click_a.location(), + gfx::Point())); + operations = textfield_->GetDragOperationsForView(textfield_, + click_a.location()); EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY, operations); - textfield_view_->WriteDragDataForView(NULL, click_a.location(), &data); + textfield_->WriteDragDataForView(NULL, click_a.location(), &data); EXPECT_TRUE(data.GetString(&string)); EXPECT_EQ(textfield_->GetSelectedText(), string); - EXPECT_TRUE(textfield_view_->GetDropFormats(&formats, &custom_formats)); + EXPECT_TRUE(textfield_->GetDropFormats(&formats, &custom_formats)); EXPECT_EQ(ui::OSExchangeData::STRING, formats); EXPECT_TRUE(custom_formats.empty()); // Drop " worl" after "h". - EXPECT_TRUE(textfield_view_->CanDrop(data)); + EXPECT_TRUE(textfield_->CanDrop(data)); gfx::Point drop_point(GetCursorPositionX(1), 0); ui::DropTargetEvent drop_a(data, drop_point, drop_point, operations); - EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE, - textfield_view_->OnDragUpdated(drop_a)); - EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE, - textfield_view_->OnPerformDrop(drop_a)); + EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE, textfield_->OnDragUpdated(drop_a)); + EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE, textfield_->OnPerformDrop(drop_a)); EXPECT_STR_EQ("h worlellod", textfield_->text()); - textfield_view_->OnDragDone(); + textfield_->OnDragDone(); // Undo/Redo the drag&drop change. SendKeyEvent(ui::VKEY_Z, false, true); @@ -1087,42 +986,42 @@ TEST_F(NativeTextfieldViewsTest, DragAndDrop_ToTheLeft) { EXPECT_STR_EQ("h worlellod", textfield_->text()); } -TEST_F(NativeTextfieldViewsTest, DragAndDrop_Canceled) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, DragAndDrop_Canceled) { + InitTextfield(); textfield_->SetText(ASCIIToUTF16("hello world")); // Start dragging "worl". textfield_->SelectRange(gfx::Range(6, 10)); gfx::Point point(GetCursorPositionX(8), 0); ui::MouseEvent click(ui::ET_MOUSE_PRESSED, point, point, - ui::EF_LEFT_MOUSE_BUTTON); - textfield_view_->OnMousePressed(click); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMousePressed(click); ui::OSExchangeData data; - textfield_view_->WriteDragDataForView(NULL, click.location(), &data); - EXPECT_TRUE(textfield_view_->CanDrop(data)); + textfield_->WriteDragDataForView(NULL, click.location(), &data); + EXPECT_TRUE(textfield_->CanDrop(data)); // Drag the text over somewhere valid, outside the current selection. gfx::Point drop_point(GetCursorPositionX(2), 0); ui::DropTargetEvent drop(data, drop_point, drop_point, ui::DragDropTypes::DRAG_MOVE); - EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE, textfield_view_->OnDragUpdated(drop)); + EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE, textfield_->OnDragUpdated(drop)); // "Cancel" the drag, via move and release over the selection, and OnDragDone. gfx::Point drag_point(GetCursorPositionX(9), 0); ui::MouseEvent drag(ui::ET_MOUSE_DRAGGED, drag_point, drag_point, - ui::EF_LEFT_MOUSE_BUTTON); + ui::EF_LEFT_MOUSE_BUTTON, 0); ui::MouseEvent release(ui::ET_MOUSE_RELEASED, drag_point, drag_point, - ui::EF_LEFT_MOUSE_BUTTON); - textfield_view_->OnMouseDragged(drag); - textfield_view_->OnMouseReleased(release); - textfield_view_->OnDragDone(); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMouseDragged(drag); + textfield_->OnMouseReleased(release); + textfield_->OnDragDone(); EXPECT_EQ(ASCIIToUTF16("hello world"), textfield_->text()); } -TEST_F(NativeTextfieldViewsTest, ReadOnlyTest) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, ReadOnlyTest) { + InitTextfield(); textfield_->SetText(ASCIIToUTF16("read only")); textfield_->SetReadOnly(true); EXPECT_TRUE(textfield_->enabled()); - EXPECT_TRUE(textfield_->focusable()); + EXPECT_TRUE(textfield_->IsFocusable()); SendKeyEvent(ui::VKEY_HOME); EXPECT_EQ(0U, textfield_->GetCursorPosition()); @@ -1140,32 +1039,32 @@ TEST_F(NativeTextfieldViewsTest, ReadOnlyTest) { EXPECT_STR_EQ("read only", textfield_->GetSelectedText()); // Cut should be disabled. - SetClipboardText("Test"); - EXPECT_FALSE(textfield_view_->IsCommandIdEnabled(IDS_APP_CUT)); - textfield_view_->ExecuteCommand(IDS_APP_CUT, 0); + SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "Test"); + EXPECT_FALSE(textfield_->IsCommandIdEnabled(IDS_APP_CUT)); + textfield_->ExecuteCommand(IDS_APP_CUT, 0); SendKeyEvent(ui::VKEY_X, false, true); SendKeyEvent(ui::VKEY_DELETE, true, false); - EXPECT_STR_EQ("Test", string16(GetClipboardText())); + EXPECT_STR_EQ("Test", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); EXPECT_STR_EQ("read only", textfield_->text()); // Paste should be disabled. - EXPECT_FALSE(textfield_view_->IsCommandIdEnabled(IDS_APP_PASTE)); - textfield_view_->ExecuteCommand(IDS_APP_PASTE, 0); + EXPECT_FALSE(textfield_->IsCommandIdEnabled(IDS_APP_PASTE)); + textfield_->ExecuteCommand(IDS_APP_PASTE, 0); SendKeyEvent(ui::VKEY_V, false, true); SendKeyEvent(ui::VKEY_INSERT, true, false); EXPECT_STR_EQ("read only", textfield_->text()); // Copy should work normally. - SetClipboardText("Test"); - EXPECT_TRUE(textfield_view_->IsCommandIdEnabled(IDS_APP_COPY)); - textfield_view_->ExecuteCommand(IDS_APP_COPY, 0); - EXPECT_STR_EQ("read only", string16(GetClipboardText())); - SetClipboardText("Test"); + SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "Test"); + EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_COPY)); + textfield_->ExecuteCommand(IDS_APP_COPY, 0); + EXPECT_STR_EQ("read only", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); + SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "Test"); SendKeyEvent(ui::VKEY_C, false, true); - EXPECT_STR_EQ("read only", string16(GetClipboardText())); - SetClipboardText("Test"); + EXPECT_STR_EQ("read only", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); + SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "Test"); SendKeyEvent(ui::VKEY_INSERT, false, true); - EXPECT_STR_EQ("read only", string16(GetClipboardText())); + EXPECT_STR_EQ("read only", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); // SetText should work even in read only mode. textfield_->SetText(ASCIIToUTF16(" four five six ")); @@ -1183,8 +1082,8 @@ TEST_F(NativeTextfieldViewsTest, ReadOnlyTest) { EXPECT_STR_EQ(" four five six ", textfield_->GetSelectedText()); } -TEST_F(NativeTextfieldViewsTest, TextInputClientTest) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, TextInputClientTest) { + InitTextfield(); ui::TextInputClient* client = textfield_->GetTextInputClient(); EXPECT_TRUE(client); EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, client->GetTextInputType()); @@ -1199,15 +1098,9 @@ TEST_F(NativeTextfieldViewsTest, TextInputClientTest) { EXPECT_TRUE(client->GetSelectionRange(&range)); EXPECT_EQ(gfx::Range(1, 4), range); - // This code can't be compiled because of a bug in base::Callback. -#if 0 - GetTextHelper helper; - base::Callback<void(string16)> callback = - base::Bind(&GetTextHelper::set_text, base::Unretained(&helper)); - - EXPECT_TRUE(client->GetTextFromRange(range, callback)); - EXPECT_STR_EQ("123", helper.text()); -#endif + base::string16 substring; + EXPECT_TRUE(client->GetTextFromRange(range, &substring)); + EXPECT_STR_EQ("123", substring); EXPECT_TRUE(client->DeleteRange(range)); EXPECT_STR_EQ("0456789", textfield_->text()); @@ -1227,8 +1120,8 @@ TEST_F(NativeTextfieldViewsTest, TextInputClientTest) { EXPECT_TRUE(client->GetCompositionTextRange(&range)); EXPECT_STR_EQ("0321456789", textfield_->text()); EXPECT_EQ(gfx::Range(1, 4), range); - EXPECT_EQ(2, on_before_user_action_); - EXPECT_EQ(2, on_after_user_action_); + EXPECT_EQ(1, on_before_user_action_); + EXPECT_EQ(1, on_after_user_action_); input_method_->SetResultTextForNextKey(UTF8ToUTF16("123")); on_before_user_action_ = on_after_user_action_ = 0; @@ -1239,8 +1132,8 @@ TEST_F(NativeTextfieldViewsTest, TextInputClientTest) { EXPECT_FALSE(client->HasCompositionText()); EXPECT_FALSE(input_method_->cancel_composition_called()); EXPECT_STR_EQ("0123456789", textfield_->text()); - EXPECT_EQ(2, on_before_user_action_); - EXPECT_EQ(2, on_after_user_action_); + EXPECT_EQ(1, on_before_user_action_); + EXPECT_EQ(1, on_after_user_action_); input_method_->Clear(); input_method_->SetCompositionTextForNextKey(composition); @@ -1283,13 +1176,13 @@ TEST_F(NativeTextfieldViewsTest, TextInputClientTest) { textfield_->SetReadOnly(false); input_method_->Clear(); - textfield_->SetObscured(true); + textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); EXPECT_TRUE(input_method_->text_input_type_changed()); EXPECT_TRUE(textfield_->GetTextInputClient()); } -TEST_F(NativeTextfieldViewsTest, UndoRedoTest) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, UndoRedoTest) { + InitTextfield(); SendKeyEvent(ui::VKEY_A); EXPECT_STR_EQ("a", textfield_->text()); SendKeyEvent(ui::VKEY_Z, false, true); @@ -1313,8 +1206,7 @@ TEST_F(NativeTextfieldViewsTest, UndoRedoTest) { // SetText SendKeyEvent(ui::VKEY_C); // Undo'ing append moves the cursor to the end for now. - // no-op SetText won't add new edit. See TextfieldViewsModel::SetText - // description. + // A no-op SetText won't add a new edit; see TextfieldModel::SetText. EXPECT_STR_EQ("abc", textfield_->text()); textfield_->SetText(ASCIIToUTF16("abc")); EXPECT_STR_EQ("abc", textfield_->text()); @@ -1383,62 +1275,70 @@ TEST_F(NativeTextfieldViewsTest, UndoRedoTest) { EXPECT_STR_EQ("", textfield_->text()); } -TEST_F(NativeTextfieldViewsTest, CutCopyPaste) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, CutCopyPaste) { + InitTextfield(); // Ensure IDS_APP_CUT cuts. textfield_->SetText(ASCIIToUTF16("123")); textfield_->SelectAll(false); - EXPECT_TRUE(textfield_view_->IsCommandIdEnabled(IDS_APP_CUT)); - textfield_view_->ExecuteCommand(IDS_APP_CUT, 0); - EXPECT_STR_EQ("123", string16(GetClipboardText())); + EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_CUT)); + textfield_->ExecuteCommand(IDS_APP_CUT, 0); + EXPECT_STR_EQ("123", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); EXPECT_STR_EQ("", textfield_->text()); + EXPECT_EQ(ui::CLIPBOARD_TYPE_COPY_PASTE, GetAndResetCopiedToClipboard()); // Ensure [Ctrl]+[x] cuts and [Ctrl]+[Alt][x] does nothing. textfield_->SetText(ASCIIToUTF16("456")); textfield_->SelectAll(false); SendKeyEvent(ui::VKEY_X, true, false, true, false); - EXPECT_STR_EQ("123", string16(GetClipboardText())); + EXPECT_STR_EQ("123", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); EXPECT_STR_EQ("456", textfield_->text()); + EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard()); SendKeyEvent(ui::VKEY_X, false, true); - EXPECT_STR_EQ("456", string16(GetClipboardText())); + EXPECT_STR_EQ("456", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); EXPECT_STR_EQ("", textfield_->text()); + EXPECT_EQ(ui::CLIPBOARD_TYPE_COPY_PASTE, GetAndResetCopiedToClipboard()); // Ensure [Shift]+[Delete] cuts. textfield_->SetText(ASCIIToUTF16("123")); textfield_->SelectAll(false); SendKeyEvent(ui::VKEY_DELETE, true, false); - EXPECT_STR_EQ("123", string16(GetClipboardText())); + EXPECT_STR_EQ("123", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); EXPECT_STR_EQ("", textfield_->text()); + EXPECT_EQ(ui::CLIPBOARD_TYPE_COPY_PASTE, GetAndResetCopiedToClipboard()); // Ensure IDS_APP_COPY copies. textfield_->SetText(ASCIIToUTF16("789")); textfield_->SelectAll(false); - EXPECT_TRUE(textfield_view_->IsCommandIdEnabled(IDS_APP_COPY)); - textfield_view_->ExecuteCommand(IDS_APP_COPY, 0); - EXPECT_STR_EQ("789", string16(GetClipboardText())); + EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_COPY)); + textfield_->ExecuteCommand(IDS_APP_COPY, 0); + EXPECT_STR_EQ("789", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); + EXPECT_EQ(ui::CLIPBOARD_TYPE_COPY_PASTE, GetAndResetCopiedToClipboard()); // Ensure [Ctrl]+[c] copies and [Ctrl]+[Alt][c] does nothing. textfield_->SetText(ASCIIToUTF16("012")); textfield_->SelectAll(false); SendKeyEvent(ui::VKEY_C, true, false, true, false); - EXPECT_STR_EQ("789", string16(GetClipboardText())); + EXPECT_STR_EQ("789", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); + EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard()); SendKeyEvent(ui::VKEY_C, false, true); - EXPECT_STR_EQ("012", string16(GetClipboardText())); + EXPECT_STR_EQ("012", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); + EXPECT_EQ(ui::CLIPBOARD_TYPE_COPY_PASTE, GetAndResetCopiedToClipboard()); // Ensure [Ctrl]+[Insert] copies. textfield_->SetText(ASCIIToUTF16("345")); textfield_->SelectAll(false); SendKeyEvent(ui::VKEY_INSERT, false, true); - EXPECT_STR_EQ("345", string16(GetClipboardText())); + EXPECT_STR_EQ("345", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); EXPECT_STR_EQ("345", textfield_->text()); + EXPECT_EQ(ui::CLIPBOARD_TYPE_COPY_PASTE, GetAndResetCopiedToClipboard()); // Ensure IDS_APP_PASTE, [Ctrl]+[V], and [Shift]+[Insert] pastes; // also ensure that [Ctrl]+[Alt]+[V] does nothing. - SetClipboardText("abc"); - textfield_->SetText(string16()); - EXPECT_TRUE(textfield_view_->IsCommandIdEnabled(IDS_APP_PASTE)); - textfield_view_->ExecuteCommand(IDS_APP_PASTE, 0); + SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "abc"); + textfield_->SetText(base::string16()); + EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_PASTE)); + textfield_->ExecuteCommand(IDS_APP_PASTE, 0); EXPECT_STR_EQ("abc", textfield_->text()); SendKeyEvent(ui::VKEY_V, false, true); EXPECT_STR_EQ("abcabc", textfield_->text()); @@ -1450,12 +1350,13 @@ TEST_F(NativeTextfieldViewsTest, CutCopyPaste) { // Ensure [Ctrl]+[Shift]+[Insert] is a no-op. textfield_->SelectAll(false); SendKeyEvent(ui::VKEY_INSERT, true, true); - EXPECT_STR_EQ("abc", string16(GetClipboardText())); + EXPECT_STR_EQ("abc", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE)); EXPECT_STR_EQ("abcabcabc", textfield_->text()); + EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard()); } -TEST_F(NativeTextfieldViewsTest, OvertypeMode) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, OvertypeMode) { + InitTextfield(); // Overtype mode should be disabled (no-op [Insert]). textfield_->SetText(ASCIIToUTF16("2")); SendKeyEvent(ui::VKEY_HOME); @@ -1464,8 +1365,8 @@ TEST_F(NativeTextfieldViewsTest, OvertypeMode) { EXPECT_STR_EQ("12", textfield_->text()); } -TEST_F(NativeTextfieldViewsTest, TextCursorDisplayTest) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, TextCursorDisplayTest) { + InitTextfield(); // LTR-RTL string in LTR context. SendKeyEvent('a'); EXPECT_STR_EQ("a", textfield_->text()); @@ -1516,11 +1417,11 @@ TEST_F(NativeTextfieldViewsTest, TextCursorDisplayTest) { EXPECT_LT(prev_x, x); } -TEST_F(NativeTextfieldViewsTest, TextCursorDisplayInRTLTest) { +TEST_F(TextfieldTest, TextCursorDisplayInRTLTest) { std::string locale = l10n_util::GetApplicationLocale(""); base::i18n::SetICUDefaultLocale("he"); - InitTextfield(Textfield::STYLE_DEFAULT); + InitTextfield(); // LTR-RTL string in RTL context. SendKeyEvent('a'); EXPECT_STR_EQ("a", textfield_->text()); @@ -1574,8 +1475,8 @@ TEST_F(NativeTextfieldViewsTest, TextCursorDisplayInRTLTest) { base::i18n::SetICUDefaultLocale(locale); } -TEST_F(NativeTextfieldViewsTest, HitInsideTextAreaTest) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, HitInsideTextAreaTest) { + InitTextfield(); textfield_->SetText(WideToUTF16(L"ab\x05E1\x5E2")); std::vector<gfx::Rect> cursor_bounds; @@ -1629,8 +1530,8 @@ TEST_F(NativeTextfieldViewsTest, HitInsideTextAreaTest) { } } -TEST_F(NativeTextfieldViewsTest, HitOutsideTextAreaTest) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, HitOutsideTextAreaTest) { + InitTextfield(); // LTR-RTL string in LTR context. textfield_->SetText(WideToUTF16(L"ab\x05E1\x5E2")); @@ -1661,11 +1562,11 @@ TEST_F(NativeTextfieldViewsTest, HitOutsideTextAreaTest) { EXPECT_EQ(bound, GetCursorBounds()); } -TEST_F(NativeTextfieldViewsTest, HitOutsideTextAreaInRTLTest) { +TEST_F(TextfieldTest, HitOutsideTextAreaInRTLTest) { std::string locale = l10n_util::GetApplicationLocale(""); base::i18n::SetICUDefaultLocale("he"); - InitTextfield(Textfield::STYLE_DEFAULT); + InitTextfield(); // RTL-LTR string in RTL context. textfield_->SetText(WideToUTF16(L"\x05E1\x5E2" L"ab")); @@ -1697,10 +1598,10 @@ TEST_F(NativeTextfieldViewsTest, HitOutsideTextAreaInRTLTest) { base::i18n::SetICUDefaultLocale(locale); } -TEST_F(NativeTextfieldViewsTest, OverflowTest) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, OverflowTest) { + InitTextfield(); - string16 str; + base::string16 str; for (int i = 0; i < 500; ++i) SendKeyEvent('a'); SendKeyEvent(kHebrewLetterSamekh); @@ -1723,13 +1624,13 @@ TEST_F(NativeTextfieldViewsTest, OverflowTest) { EXPECT_EQ(501U, textfield_->GetCursorPosition()); } -TEST_F(NativeTextfieldViewsTest, OverflowInRTLTest) { +TEST_F(TextfieldTest, OverflowInRTLTest) { std::string locale = l10n_util::GetApplicationLocale(""); base::i18n::SetICUDefaultLocale("he"); - InitTextfield(Textfield::STYLE_DEFAULT); + InitTextfield(); - string16 str; + base::string16 str; for (int i = 0; i < 500; ++i) SendKeyEvent('a'); SendKeyEvent(kHebrewLetterSamekh); @@ -1754,10 +1655,10 @@ TEST_F(NativeTextfieldViewsTest, OverflowInRTLTest) { base::i18n::SetICUDefaultLocale(locale); } -TEST_F(NativeTextfieldViewsTest, GetCompositionCharacterBoundsTest) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, GetCompositionCharacterBoundsTest) { + InitTextfield(); - string16 str; + base::string16 str; const uint32 char_count = 10UL; ui::CompositionText composition; composition.text = UTF8ToUTF16("0123456789"); @@ -1777,8 +1678,8 @@ TEST_F(NativeTextfieldViewsTest, GetCompositionCharacterBoundsTest) { gfx::Rect cursor_bounds = GetCursorBounds(); gfx::Point top_left(prev_cursor.x(), prev_cursor.y()); gfx::Point bottom_right(cursor_bounds.x(), prev_cursor.bottom()); - views::View::ConvertPointToScreen(textfield_view_, &top_left); - views::View::ConvertPointToScreen(textfield_view_, &bottom_right); + views::View::ConvertPointToScreen(textfield_, &top_left); + views::View::ConvertPointToScreen(textfield_, &bottom_right); char_rect_in_screen_coord[i].set_origin(top_left); char_rect_in_screen_coord[i].set_width(bottom_right.x() - top_left.x()); char_rect_in_screen_coord[i].set_height(bottom_right.y() - top_left.y()); @@ -1798,10 +1699,10 @@ TEST_F(NativeTextfieldViewsTest, GetCompositionCharacterBoundsTest) { EXPECT_FALSE(client->GetCompositionCharacterBounds(char_count + 100, &rect)); } -TEST_F(NativeTextfieldViewsTest, GetCompositionCharacterBounds_ComplexText) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, GetCompositionCharacterBounds_ComplexText) { + InitTextfield(); - const char16 kUtf16Chars[] = { + const base::char16 kUtf16Chars[] = { // U+0020 SPACE 0x0020, // U+1F408 (CAT) as surrogate pair @@ -1835,8 +1736,8 @@ TEST_F(NativeTextfieldViewsTest, GetCompositionCharacterBounds_ComplexText) { // The word we select by double clicking should remain selected regardless of // where we drag the mouse afterwards without releasing the left button. -TEST_F(NativeTextfieldViewsTest, KeepInitiallySelectedWord) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, KeepInitiallySelectedWord) { + InitTextfield(); textfield_->SetText(ASCIIToUTF16("abc def ghi")); @@ -1850,73 +1751,192 @@ TEST_F(NativeTextfieldViewsTest, KeepInitiallySelectedWord) { const gfx::Point middle(middle_cursor.x(), middle_cursor.y() + middle_cursor.height() / 2); ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, middle, middle, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); - textfield_view_->OnMousePressed(press_event); + textfield_->OnMousePressed(press_event); EXPECT_EQ(gfx::Range(4, 7), textfield_->GetSelectedRange()); // Drag the mouse to the beginning of the textfield. ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, beginning, beginning, - ui::EF_LEFT_MOUSE_BUTTON); - textfield_view_->OnMouseDragged(drag_event); + ui::EF_LEFT_MOUSE_BUTTON, 0); + textfield_->OnMouseDragged(drag_event); EXPECT_EQ(gfx::Range(7, 0), textfield_->GetSelectedRange()); } -// Touch selection and draggin currently only works for chromeos. +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) +TEST_F(TextfieldTest, SelectionClipboard) { + InitTextfield(); + textfield_->SetText(ASCIIToUTF16("0123")); + gfx::Point point_1(GetCursorPositionX(1), 0); + gfx::Point point_2(GetCursorPositionX(2), 0); + gfx::Point point_3(GetCursorPositionX(3), 0); + gfx::Point point_4(GetCursorPositionX(4), 0); + + // Text selected by the mouse should be placed on the selection clipboard. + ui::MouseEvent press(ui::ET_MOUSE_PRESSED, point_1, point_1, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMousePressed(press); + ui::MouseEvent drag(ui::ET_MOUSE_DRAGGED, point_3, point_3, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMouseDragged(drag); + ui::MouseEvent release(ui::ET_MOUSE_RELEASED, point_3, point_3, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMouseReleased(release); + EXPECT_EQ(gfx::Range(1, 3), textfield_->GetSelectedRange()); + EXPECT_STR_EQ("12", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + + // Select-all should update the selection clipboard. + SendKeyEvent(ui::VKEY_A, false, true); + EXPECT_EQ(gfx::Range(0, 4), textfield_->GetSelectedRange()); + EXPECT_STR_EQ("0123", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard()); + + // Shift-click selection modifications should update the clipboard. + NonClientMouseClick(); + ui::MouseEvent press_2(ui::ET_MOUSE_PRESSED, point_2, point_2, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + press_2.set_flags(press_2.flags() | ui::EF_SHIFT_DOWN); + textfield_->OnMousePressed(press_2); + ui::MouseEvent release_2(ui::ET_MOUSE_RELEASED, point_2, point_2, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMouseReleased(release_2); + EXPECT_EQ(gfx::Range(0, 2), textfield_->GetSelectedRange()); + EXPECT_STR_EQ("01", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard()); + + // Shift-Left/Right should update the selection clipboard. + SendKeyEvent(ui::VKEY_RIGHT, true, false); + EXPECT_STR_EQ("012", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard()); + SendKeyEvent(ui::VKEY_LEFT, true, false); + EXPECT_STR_EQ("01", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard()); + SendKeyEvent(ui::VKEY_RIGHT, true, true); + EXPECT_STR_EQ("0123", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard()); + + // Moving the cursor without a selection should not change the clipboard. + SendKeyEvent(ui::VKEY_LEFT, false, false); + EXPECT_EQ(gfx::Range(0, 0), textfield_->GetSelectedRange()); + EXPECT_STR_EQ("0123", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard()); + + // Middle clicking should paste at the mouse (not cursor) location. + ui::MouseEvent middle(ui::ET_MOUSE_PRESSED, point_4, point_4, + ui::EF_MIDDLE_MOUSE_BUTTON, ui::EF_MIDDLE_MOUSE_BUTTON); + textfield_->OnMousePressed(middle); + EXPECT_STR_EQ("01230123", textfield_->text()); + EXPECT_EQ(gfx::Range(0, 0), textfield_->GetSelectedRange()); + EXPECT_STR_EQ("0123", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + + // Middle click pasting should adjust trailing cursors. + textfield_->SelectRange(gfx::Range(5, 5)); + textfield_->OnMousePressed(middle); + EXPECT_STR_EQ("012301230123", textfield_->text()); + EXPECT_EQ(gfx::Range(9, 9), textfield_->GetSelectedRange()); + + // Middle click pasting should adjust trailing selections. + textfield_->SelectRange(gfx::Range(7, 9)); + textfield_->OnMousePressed(middle); + EXPECT_STR_EQ("0123012301230123", textfield_->text()); + EXPECT_EQ(gfx::Range(11, 13), textfield_->GetSelectedRange()); + + // Middle clicking in the selection should clear the clipboard and selection. + textfield_->SelectRange(gfx::Range(2, 6)); + textfield_->OnMousePressed(middle); + EXPECT_STR_EQ("0123012301230123", textfield_->text()); + EXPECT_EQ(gfx::Range(6, 6), textfield_->GetSelectedRange()); + EXPECT_TRUE(GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION).empty()); + + // Double and triple clicking should update the clipboard contents. + textfield_->SetText(ASCIIToUTF16("ab cd ef")); + gfx::Point word(GetCursorPositionX(4), 0); + ui::MouseEvent press_word(ui::ET_MOUSE_PRESSED, word, word, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMousePressed(press_word); + ui::MouseEvent release_word(ui::ET_MOUSE_RELEASED, word, word, + ui::EF_LEFT_MOUSE_BUTTON, + ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMouseReleased(release_word); + ui::MouseEvent double_click(ui::ET_MOUSE_PRESSED, word, word, + ui::EF_LEFT_MOUSE_BUTTON | ui::EF_IS_DOUBLE_CLICK, + ui::EF_LEFT_MOUSE_BUTTON); + textfield_->OnMousePressed(double_click); + textfield_->OnMouseReleased(release_word); + EXPECT_EQ(gfx::Range(3, 5), textfield_->GetSelectedRange()); + EXPECT_STR_EQ("cd", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard()); + textfield_->OnMousePressed(press_word); + textfield_->OnMouseReleased(release_word); + EXPECT_EQ(gfx::Range(0, 8), textfield_->GetSelectedRange()); + EXPECT_STR_EQ("ab cd ef", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard()); + + // Selecting a range of text without any user interaction should not change + // the clipboard content. + textfield_->SelectRange(gfx::Range(0, 3)); + EXPECT_STR_EQ("ab ", textfield_->GetSelectedText()); + EXPECT_STR_EQ("ab cd ef", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard()); + + SetClipboardText(ui::CLIPBOARD_TYPE_SELECTION, "other"); + textfield_->SelectAll(false); + EXPECT_STR_EQ("other", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION)); + EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard()); +} +#endif + +// Touch selection and dragging currently only works for chromeos. #if defined(OS_CHROMEOS) -TEST_F(NativeTextfieldViewsTest, TouchSelectionAndDraggingTest) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, TouchSelectionAndDraggingTest) { + InitTextfield(); textfield_->SetText(ASCIIToUTF16("hello world")); - EXPECT_FALSE(GetTouchSelectionController()); - const int eventX = GetCursorPositionX(2); - const int eventY = 0; + EXPECT_FALSE(test_api_->touch_selection_controller()); + const int x = GetCursorPositionX(2); + GestureEventForTest tap(ui::ET_GESTURE_TAP, x, 0, 1.0f, 0.0f); + GestureEventForTest tap_down(ui::ET_GESTURE_TAP_DOWN, x, 0, 0.0f, 0.0f); + GestureEventForTest long_press(ui::ET_GESTURE_LONG_PRESS, x, 0, 0.0f, 0.0f); CommandLine::ForCurrentProcess()->AppendSwitch(switches::kEnableTouchEditing); // Tapping on the textfield should turn on the TouchSelectionController. - GestureEventForTest tap(ui::ET_GESTURE_TAP, eventX, eventY, 1.0f, 0.0f); - textfield_view_->OnGestureEvent(&tap); - EXPECT_TRUE(GetTouchSelectionController()); + textfield_->OnGestureEvent(&tap); + EXPECT_TRUE(test_api_->touch_selection_controller()); // Un-focusing the textfield should reset the TouchSelectionController - textfield_view_->GetFocusManager()->ClearFocus(); - EXPECT_FALSE(GetTouchSelectionController()); + textfield_->GetFocusManager()->ClearFocus(); + EXPECT_FALSE(test_api_->touch_selection_controller()); // With touch editing enabled, long press should not show context menu. // Instead, select word and invoke TouchSelectionController. - GestureEventForTest tap_down(ui::ET_GESTURE_TAP_DOWN, eventX, eventY, 0.0f, - 0.0f); - textfield_view_->OnGestureEvent(&tap_down); - GestureEventForTest long_press(ui::ET_GESTURE_LONG_PRESS, eventX, eventY, - 0.0f, 0.0f); - textfield_view_->OnGestureEvent(&long_press); + textfield_->OnGestureEvent(&tap_down); + textfield_->OnGestureEvent(&long_press); EXPECT_STR_EQ("hello", textfield_->GetSelectedText()); - EXPECT_TRUE(GetTouchSelectionController()); + EXPECT_TRUE(test_api_->touch_selection_controller()); - // Long pressing again in the selecting region should not do anything since - // touch drag drop is not yet enabled. - textfield_view_->OnGestureEvent(&tap_down); - textfield_view_->OnGestureEvent(&long_press); + // With touch drag drop enabled, long pressing in the selected region should + // start a drag and remove TouchSelectionController. + ASSERT_TRUE(switches::IsTouchDragDropEnabled()); + textfield_->OnGestureEvent(&tap_down); + textfield_->OnGestureEvent(&long_press); EXPECT_STR_EQ("hello", textfield_->GetSelectedText()); - EXPECT_TRUE(GetTouchSelectionController()); - EXPECT_TRUE(long_press.handled()); + EXPECT_FALSE(test_api_->touch_selection_controller()); - // After enabling touch drag drop, long pressing in the selected region should - // start a drag and remove TouchSelectionController. + // After disabling touch drag drop, long pressing again in the selection + // region should not do anything. CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kEnableTouchDragDrop); - textfield_view_->OnGestureEvent(&tap_down); - - // Create a new long press event since the previous one is not marked handled. - GestureEventForTest long_press2(ui::ET_GESTURE_LONG_PRESS, eventX, eventY, - 0.0f, 0.0f); - textfield_view_->OnGestureEvent(&long_press2); + switches::kDisableTouchDragDrop); + ASSERT_FALSE(switches::IsTouchDragDropEnabled()); + textfield_->OnGestureEvent(&tap_down); + textfield_->OnGestureEvent(&long_press); EXPECT_STR_EQ("hello", textfield_->GetSelectedText()); - EXPECT_FALSE(GetTouchSelectionController()); + EXPECT_TRUE(test_api_->touch_selection_controller()); + EXPECT_TRUE(long_press.handled()); } -TEST_F(NativeTextfieldViewsTest, TouchScrubbingSelection) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, TouchScrubbingSelection) { + InitTextfield(); textfield_->SetText(ASCIIToUTF16("hello world")); - EXPECT_FALSE(GetTouchSelectionController()); + EXPECT_FALSE(test_api_->touch_selection_controller()); CommandLine::ForCurrentProcess()->AppendSwitch(switches::kEnableTouchEditing); @@ -1926,37 +1946,37 @@ TEST_F(NativeTextfieldViewsTest, TouchScrubbingSelection) { GestureEventForTest tap_down(ui::ET_GESTURE_TAP_DOWN, scrubbing_start, 0, 0.0f, 0.0f); - textfield_view_->OnGestureEvent(&tap_down); + textfield_->OnGestureEvent(&tap_down); GestureEventForTest tap_cancel(ui::ET_GESTURE_TAP_CANCEL, scrubbing_start, 0, 0.0f, 0.0f); - textfield_view_->OnGestureEvent(&tap_cancel); + textfield_->OnGestureEvent(&tap_cancel); GestureEventForTest scroll_begin(ui::ET_GESTURE_SCROLL_BEGIN, scrubbing_start, 0, 0.0f, 0.0f); - textfield_view_->OnGestureEvent(&scroll_begin); + textfield_->OnGestureEvent(&scroll_begin); GestureEventForTest scroll_update(ui::ET_GESTURE_SCROLL_UPDATE, scrubbing_end, 0, scrubbing_end - scrubbing_start, 0.0f); - textfield_view_->OnGestureEvent(&scroll_update); + textfield_->OnGestureEvent(&scroll_update); GestureEventForTest scroll_end(ui::ET_GESTURE_SCROLL_END, scrubbing_end, 0, 0.0f, 0.0f); - textfield_view_->OnGestureEvent(&scroll_end); + textfield_->OnGestureEvent(&scroll_end); GestureEventForTest end(ui::ET_GESTURE_END, scrubbing_end, 0, 0.0f, 0.0f); - textfield_view_->OnGestureEvent(&end); + textfield_->OnGestureEvent(&end); // In the end, part of text should have been selected and handles should have // appeared. EXPECT_STR_EQ("ello ", textfield_->GetSelectedText()); - EXPECT_TRUE(GetTouchSelectionController()); + EXPECT_TRUE(test_api_->touch_selection_controller()); } #endif -// Long_Press gesture in NativeTextfieldViews can initiate a drag and drop now. -TEST_F(NativeTextfieldViewsTest, TestLongPressInitiatesDragDrop) { - InitTextfield(Textfield::STYLE_DEFAULT); +// Long_Press gesture in Textfield can initiate a drag and drop now. +TEST_F(TextfieldTest, TestLongPressInitiatesDragDrop) { + InitTextfield(); textfield_->SetText(ASCIIToUTF16("Hello string world")); // Ensure the textfield will provide selected text for drag data. @@ -1970,13 +1990,13 @@ TEST_F(NativeTextfieldViewsTest, TestLongPressInitiatesDragDrop) { // Create a long press event in the selected region should start a drag. GestureEventForTest long_press(ui::ET_GESTURE_LONG_PRESS, kStringPoint.x(), kStringPoint.y(), 0.0f, 0.0f); - textfield_view_->OnGestureEvent(&long_press); - EXPECT_TRUE(textfield_view_->CanStartDragForView(NULL, - kStringPoint, kStringPoint)); + textfield_->OnGestureEvent(&long_press); + EXPECT_TRUE(textfield_->CanStartDragForView(NULL, kStringPoint, + kStringPoint)); } -TEST_F(NativeTextfieldViewsTest, GetTextfieldBaseline_FontFallbackTest) { - InitTextfield(Textfield::STYLE_DEFAULT); +TEST_F(TextfieldTest, GetTextfieldBaseline_FontFallbackTest) { + InitTextfield(); textfield_->SetText(UTF8ToUTF16("abc")); const int old_baseline = textfield_->GetBaseline(); diff --git a/chromium/ui/views/controls/throbber.cc b/chromium/ui/views/controls/throbber.cc index 25272de0536..5df35606378 100644 --- a/chromium/ui/views/controls/throbber.cc +++ b/chromium/ui/views/controls/throbber.cc @@ -68,7 +68,7 @@ void Throbber::Run() { SchedulePaint(); } -gfx::Size Throbber::GetPreferredSize() { +gfx::Size Throbber::GetPreferredSize() const { return gfx::Size(frames_->height(), frames_->height()); } diff --git a/chromium/ui/views/controls/throbber.h b/chromium/ui/views/controls/throbber.h index 8ae3895a22a..08188c39cf5 100644 --- a/chromium/ui/views/controls/throbber.h +++ b/chromium/ui/views/controls/throbber.h @@ -37,7 +37,7 @@ class VIEWS_EXPORT Throbber : public View { void SetFrames(const gfx::ImageSkia* frames); // Overridden from View: - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; protected: diff --git a/chromium/ui/views/controls/tree/tree_view.cc b/chromium/ui/views/controls/tree/tree_view.cc index 2c7bd55c396..f514fe45fcd 100644 --- a/chromium/ui/views/controls/tree/tree_view.cc +++ b/chromium/ui/views/controls/tree/tree_view.cc @@ -9,11 +9,12 @@ #include "base/i18n/rtl.h" #include "base/message_loop/message_loop.h" #include "grit/ui_resources.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/base/resource/resource_bundle.h" #include "ui/events/event.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/canvas.h" +#include "ui/gfx/geometry/rect.h" #include "ui/gfx/image/image.h" #include "ui/gfx/rect_conversions.h" #include "ui/gfx/skia_util.h" @@ -78,8 +79,7 @@ TreeView::TreeView() editable_(true), controller_(NULL), root_shown_(true), - has_custom_icons_(false), - row_height_(font_.GetHeight() + kTextVerticalPadding * 2) { + row_height_(font_list_.GetHeight() + kTextVerticalPadding * 2) { SetFocusable(true); closed_icon_ = *ui::ResourceBundle::GetSharedInstance().GetImageNamed( (base::i18n::IsRTL() ? IDR_FOLDER_CLOSED_RTL @@ -158,9 +158,9 @@ void TreeView::StartEditing(TreeModelNode* node) { // Add the editor immediately as GetPreferredSize returns the wrong thing if // not parented. AddChildView(editor_); - editor_->SetFont(font_); + editor_->SetFontList(font_list_); empty_editor_size_ = editor_->GetPreferredSize(); - editor_->SetController(this); + editor_->set_controller(this); } editor_->SetText(selected_node_->model_node()->GetTitle()); LayoutEditor(); @@ -245,7 +245,7 @@ void TreeView::SetSelectedNode(TreeModelNode* model_node) { if (changed) { // TODO(dmazzoni): Decide if EVENT_SELECTION_CHANGED is a better choice for // sub-item selection event. - NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_FOCUS, true); + NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true); } } @@ -350,7 +350,7 @@ void TreeView::Layout() { LayoutEditor(); } -gfx::Size TreeView::GetPreferredSize() { +gfx::Size TreeView::GetPreferredSize() const { return preferred_size_; } @@ -403,14 +403,14 @@ void TreeView::ShowContextMenu(const gfx::Point& p, View::ShowContextMenu(p, source_type); } -void TreeView::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_OUTLINE; - state->state = ui::AccessibilityTypes::STATE_READONLY; +void TreeView::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_TREE; + state->AddStateFlag(ui::AX_STATE_READ_ONLY); if (!selected_node_) return; // Get selected item info. - state->role = ui::AccessibilityTypes::ROLE_OUTLINEITEM; + state->role = ui::AX_ROLE_TREE_ITEM; state->name = selected_node_->model_node()->GetTitle(); } @@ -482,7 +482,7 @@ void TreeView::TreeNodeChanged(TreeModel* model, TreeModelNode* model_node) { } void TreeView::ContentsChanged(Textfield* sender, - const string16& new_contents) { + const base::string16& new_contents) { } bool TreeView::HandleKeyEvent(Textfield* sender, @@ -525,7 +525,7 @@ void TreeView::SetSelectedRow(int row) { SetSelectedNode(GetNodeForRow(row)); } -string16 TreeView::GetTextForRow(int row) { +base::string16 TreeView::GetTextForRow(int row) { return GetNodeForRow(row)->GetTitle(); } @@ -694,7 +694,7 @@ void TreeView::ConfigureInternalNode(TreeModelNode* model_node, void TreeView::UpdateNodeTextWidth(InternalNode* node) { int width = 0, height = 0; - gfx::Canvas::SizeStringInt(node->model_node()->GetTitle(), font_, + gfx::Canvas::SizeStringInt(node->model_node()->GetTitle(), font_list_, &width, &height, 0, gfx::Canvas::NO_ELLIPSIS); node->set_text_width(width); } @@ -729,7 +729,7 @@ void TreeView::LayoutEditor() { row_bounds.set_width(row_bounds.width() - text_offset_); row_bounds.Inset(kTextHorizontalPadding, kTextVerticalPadding); row_bounds.Inset(-empty_editor_size_.width() / 2, - -(empty_editor_size_.height() - font_.GetHeight()) / 2); + -(empty_editor_size_.height() - font_list_.GetHeight()) / 2); // Give a little extra space for editing. row_bounds.set_width(row_bounds.width() + 50); editor_->SetBoundsRect(row_bounds); @@ -803,12 +803,14 @@ void TreeView::PaintRow(gfx::Canvas* canvas, } const ui::NativeTheme::ColorId color_id = text_color_id(HasFocus(), node == selected_node_); - canvas->DrawStringInt(node->model_node()->GetTitle(), font_, - GetNativeTheme()->GetSystemColor(color_id), - text_bounds.x() + kTextHorizontalPadding, - text_bounds.y() + kTextVerticalPadding, - text_bounds.width() - kTextHorizontalPadding * 2, - text_bounds.height() - kTextVerticalPadding * 2); + const gfx::Rect internal_bounds( + text_bounds.x() + kTextHorizontalPadding, + text_bounds.y() + kTextVerticalPadding, + text_bounds.width() - kTextHorizontalPadding * 2, + text_bounds.height() - kTextVerticalPadding * 2); + canvas->DrawStringRect(node->model_node()->GetTitle(), font_list_, + GetNativeTheme()->GetSystemColor(color_id), + internal_bounds); } } diff --git a/chromium/ui/views/controls/tree/tree_view.h b/chromium/ui/views/controls/tree/tree_view.h index de571c789bf..3cae3b52376 100644 --- a/chromium/ui/views/controls/tree/tree_view.h +++ b/chromium/ui/views/controls/tree/tree_view.h @@ -11,13 +11,17 @@ #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" #include "ui/base/models/tree_node_model.h" -#include "ui/gfx/font.h" +#include "ui/gfx/font_list.h" #include "ui/gfx/image/image_skia.h" #include "ui/views/controls/prefix_delegate.h" #include "ui/views/controls/textfield/textfield_controller.h" #include "ui/views/focus/focus_manager.h" #include "ui/views/view.h" +namespace gfx { +class Rect; +} // namespace gfx + namespace views { class Textfield; @@ -117,14 +121,14 @@ class VIEWS_EXPORT TreeView : public ui::TreeModelObserver, // View overrides: virtual void Layout() OVERRIDE; - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE; virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE; virtual ui::TextInputClient* GetTextInputClient() OVERRIDE; virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; virtual void ShowContextMenu(const gfx::Point& p, ui::MenuSourceType source_type) OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; virtual const char* GetClassName() const OVERRIDE; // TreeModelObserver overrides: @@ -141,7 +145,7 @@ class VIEWS_EXPORT TreeView : public ui::TreeModelObserver, // TextfieldController overrides: virtual void ContentsChanged(Textfield* sender, - const string16& new_contents) OVERRIDE; + const base::string16& new_contents) OVERRIDE; virtual bool HandleKeyEvent(Textfield* sender, const ui::KeyEvent& key_event) OVERRIDE; @@ -155,7 +159,7 @@ class VIEWS_EXPORT TreeView : public ui::TreeModelObserver, virtual int GetRowCount() OVERRIDE; virtual int GetSelectedRow() OVERRIDE; virtual void SetSelectedRow(int row) OVERRIDE; - virtual string16 GetTextForRow(int row) OVERRIDE; + virtual base::string16 GetTextForRow(int row) OVERRIDE; protected: // View overrides: @@ -370,14 +374,11 @@ class VIEWS_EXPORT TreeView : public ui::TreeModelObserver, // Whether or not the root is shown in the tree. bool root_shown_; - // Did the model return a non-empty set of icons from GetIcons? - bool has_custom_icons_; - // Cached preferred size. gfx::Size preferred_size_; - // Font used to display text. - gfx::Font font_; + // Font list used to display text. + gfx::FontList font_list_; // Height of each row. Based on font and some padding. int row_height_; diff --git a/chromium/ui/views/controls/tree/tree_view_unittest.cc b/chromium/ui/views/controls/tree/tree_view_unittest.cc index 1864ff9a9bf..bf73da99ec1 100644 --- a/chromium/ui/views/controls/tree/tree_view_unittest.cc +++ b/chromium/ui/views/controls/tree/tree_view_unittest.cc @@ -17,6 +17,8 @@ using ui::TreeModel; using ui::TreeModelNode; using ui::TreeNode; +using base::ASCIIToUTF16; + namespace views { class TestNode : public TreeNode<TestNode> { @@ -68,7 +70,7 @@ class TreeViewTest : public ViewsTestBase { private: std::string InternalNodeAsString(TreeView::InternalNode* node); - TestNode* GetNodeByTitleImpl(TestNode* node, const string16& title); + TestNode* GetNodeByTitleImpl(TestNode* node, const base::string16& title); DISALLOW_COPY_AND_ASSIGN(TreeViewTest); }; @@ -88,12 +90,14 @@ std::string TreeViewTest::TreeViewContentsAsString() { std::string TreeViewTest::GetSelectedNodeTitle() { TreeModelNode* model_node = tree_.GetSelectedNode(); - return model_node ? UTF16ToASCII(model_node->GetTitle()) : std::string(); + return model_node ? base::UTF16ToASCII(model_node->GetTitle()) + : std::string(); } std::string TreeViewTest::GetEditingNodeTitle() { TreeModelNode* model_node = tree_.GetEditingNode(); - return model_node ? UTF16ToASCII(model_node->GetTitle()) : std::string(); + return model_node ? base::UTF16ToASCII(model_node->GetTitle()) + : std::string(); } TestNode* TreeViewTest::GetNodeByTitle(const std::string& title) { @@ -118,7 +122,7 @@ int TreeViewTest::GetRowCount() { } TestNode* TreeViewTest::GetNodeByTitleImpl(TestNode* node, - const string16& title) { + const base::string16& title) { if (node->GetTitle() == title) return node; for (int i = 0; i < node->child_count(); ++i) { @@ -131,7 +135,7 @@ TestNode* TreeViewTest::GetNodeByTitleImpl(TestNode* node, std::string TreeViewTest::InternalNodeAsString( TreeView::InternalNode* node) { - std::string result = UTF16ToASCII(node->model_node()->GetTitle()); + std::string result = base::UTF16ToASCII(node->model_node()->GetTitle()); if (node->is_expanded() && node->child_count()) { result += " ["; for (int i = 0; i < node->child_count(); ++i) { diff --git a/chromium/ui/views/controls/webview/unhandled_keyboard_event_handler_win.cc b/chromium/ui/views/controls/webview/unhandled_keyboard_event_handler_win.cc index c1148c74e44..463ef7e3a05 100644 --- a/chromium/ui/views/controls/webview/unhandled_keyboard_event_handler_win.cc +++ b/chromium/ui/views/controls/webview/unhandled_keyboard_event_handler_win.cc @@ -57,13 +57,9 @@ void UnhandledKeyboardEventHandler::HandleKeyboardEvent( // Any unhandled keyboard/character messages should be defproced. // This allows stuff like F10, etc to work correctly. -#if defined(USE_AURA) if (!event.os_event) return; const MSG& message(event.os_event->native_event()); -#else - const MSG& message(event.os_event); -#endif DefWindowProc(message.hwnd, message.message, message.wParam, message.lParam); } diff --git a/chromium/ui/views/controls/webview/web_dialog_view.cc b/chromium/ui/views/controls/webview/web_dialog_view.cc index 7f88f022f55..ffb5cbafbde 100644 --- a/chromium/ui/views/controls/webview/web_dialog_view.cc +++ b/chromium/ui/views/controls/webview/web_dialog_view.cc @@ -12,22 +12,18 @@ #include "content/public/browser/notification_details.h" #include "content/public/browser/notification_source.h" #include "content/public/browser/notification_types.h" -#include "content/public/browser/render_view_host.h" +#include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" +#include "ui/events/event.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/views/controls/webview/webview.h" #include "ui/views/layout/fill_layout.h" +#include "ui/views/widget/native_widget_private.h" #include "ui/views/widget/root_view.h" #include "ui/views/widget/widget.h" #include "ui/web_dialogs/web_dialog_delegate.h" #include "ui/web_dialogs/web_dialog_ui.h" -#if defined(USE_AURA) -#include "ui/events/event.h" -#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" -#include "ui/views/widget/native_widget_aura.h" -#endif - using content::NativeWebKeyboardEvent; using content::WebContents; using content::WebUIMessageHandler; @@ -46,7 +42,6 @@ WebDialogView::WebDialogView( WebContentsHandler* handler) : ClientView(NULL, NULL), WebDialogWebContentsDelegate(context, handler), - initialized_(false), delegate_(delegate), web_view_(new views::WebView(context)), is_attempting_close_dialog_(false), @@ -71,14 +66,14 @@ content::WebContents* WebDialogView::web_contents() { //////////////////////////////////////////////////////////////////////////////// // WebDialogView, views::View implementation: -gfx::Size WebDialogView::GetPreferredSize() { +gfx::Size WebDialogView::GetPreferredSize() const { gfx::Size out; if (delegate_) delegate_->GetDialogSize(&out); return out; } -gfx::Size WebDialogView::GetMinimumSize() { +gfx::Size WebDialogView::GetMinimumSize() const { gfx::Size out; if (delegate_) delegate_->GetMinimumDialogSize(&out); @@ -118,8 +113,7 @@ bool WebDialogView::CanClose() { if (!is_attempting_close_dialog_) { // Fire beforeunload event when user attempts to close the dialog. is_attempting_close_dialog_ = true; - web_view_-> - web_contents()->GetRenderViewHost()->FirePageBeforeUnload(false); + web_view_->web_contents()->DispatchBeforeUnload(false); } return false; } @@ -128,6 +122,8 @@ bool WebDialogView::CanClose() { // WebDialogView, views::WidgetDelegate implementation: bool WebDialogView::CanResize() const { + if (delegate_) + return delegate_->CanResizeDialog(); return true; } @@ -135,10 +131,10 @@ ui::ModalType WebDialogView::GetModalType() const { return GetDialogModalType(); } -string16 WebDialogView::GetWindowTitle() const { +base::string16 WebDialogView::GetWindowTitle() const { if (delegate_) return delegate_->GetDialogTitle(); - return string16(); + return base::string16(); } std::string WebDialogView::GetWindowName() const { @@ -188,7 +184,7 @@ ui::ModalType WebDialogView::GetDialogModalType() const { return ui::MODAL_TYPE_NONE; } -string16 WebDialogView::GetDialogTitle() const { +base::string16 WebDialogView::GetDialogTitle() const { return GetWindowTitle(); } @@ -282,23 +278,10 @@ void WebDialogView::MoveContents(WebContents* source, const gfx::Rect& pos) { // they're all browser-specific. (This may change in the future.) void WebDialogView::HandleKeyboardEvent(content::WebContents* source, const NativeWebKeyboardEvent& event) { -#if defined(USE_AURA) if (!event.os_event) return; - ui::KeyEvent aura_event(event.os_event->native_event(), false); - ui::EventHandler* event_handler = - GetWidget()->native_widget()->GetEventHandler(); - - DCHECK(event_handler); - if (event_handler) - event_handler->OnKeyEvent(&aura_event); -#elif defined(OS_WIN) - // Any unhandled keyboard/character messages should be defproced. - // This allows stuff like F10, etc to work correctly. - DefWindowProc(event.os_event.hwnd, event.os_event.message, - event.os_event.wParam, event.os_event.lParam); -#endif + GetWidget()->native_widget_private()->RepostNativeEvent(event.os_event); } void WebDialogView::CloseContents(WebContents* source) { @@ -335,7 +318,8 @@ void WebDialogView::AddNewContents(content::WebContents* source, was_blocked); } -void WebDialogView::LoadingStateChanged(content::WebContents* source) { +void WebDialogView::LoadingStateChanged(content::WebContents* source, + bool to_different_document) { if (delegate_) delegate_->OnLoadingStateChanged(source); } diff --git a/chromium/ui/views/controls/webview/web_dialog_view.h b/chromium/ui/views/controls/webview/web_dialog_view.h index bb2eb395e52..2e69ad314ba 100644 --- a/chromium/ui/views/controls/webview/web_dialog_view.h +++ b/chromium/ui/views/controls/webview/web_dialog_view.h @@ -52,8 +52,8 @@ class WEBVIEW_EXPORT WebDialogView : public views::ClientView, content::WebContents* web_contents(); // Overridden from views::ClientView: - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual gfx::Size GetMinimumSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual gfx::Size GetMinimumSize() const OVERRIDE; virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE; virtual void ViewHierarchyChanged( @@ -63,7 +63,7 @@ class WEBVIEW_EXPORT WebDialogView : public views::ClientView, // Overridden from views::WidgetDelegate: virtual bool CanResize() const OVERRIDE; virtual ui::ModalType GetModalType() const OVERRIDE; - virtual string16 GetWindowTitle() const OVERRIDE; + virtual base::string16 GetWindowTitle() const OVERRIDE; virtual std::string GetWindowName() const OVERRIDE; virtual void WindowClosing() OVERRIDE; virtual views::View* GetContentsView() OVERRIDE; @@ -75,7 +75,7 @@ class WEBVIEW_EXPORT WebDialogView : public views::ClientView, // Overridden from ui::WebDialogDelegate: virtual ui::ModalType GetDialogModalType() const OVERRIDE; - virtual string16 GetDialogTitle() const OVERRIDE; + virtual base::string16 GetDialogTitle() const OVERRIDE; virtual GURL GetDialogContentURL() const OVERRIDE; virtual void GetWebUIMessageHandlers( std::vector<content::WebUIMessageHandler*>* handlers) const OVERRIDE; @@ -110,7 +110,8 @@ class WEBVIEW_EXPORT WebDialogView : public views::ClientView, const gfx::Rect& initial_pos, bool user_gesture, bool* was_blocked) OVERRIDE; - virtual void LoadingStateChanged(content::WebContents* source) OVERRIDE; + virtual void LoadingStateChanged(content::WebContents* source, + bool to_different_document) OVERRIDE; virtual void BeforeUnloadFired(content::WebContents* tab, bool proceed, bool* proceed_to_fire_unload) OVERRIDE; @@ -121,11 +122,6 @@ class WEBVIEW_EXPORT WebDialogView : public views::ClientView, // Initializes the contents of the dialog. void InitDialog(); - // Whether the view is initialized. That is, dialog accelerators is registered - // and FreezeUpdates property is set to prevent WM from showing the window - // until the property is removed. - bool initialized_; - // This view is a delegate to the HTML content since it needs to get notified // about when the dialog is closing. For all other actions (besides dialog // closing) we delegate to the creator of this view, which we keep track of @@ -142,11 +138,11 @@ class WEBVIEW_EXPORT WebDialogView : public views::ClientView, // beforeunload event. bool before_unload_fired_; - // Whether the dialog is closed from WebUI in response to a "DialogClose" + // Whether the dialog is closed from WebUI in response to a "dialogClose" // message. bool closed_via_webui_; - // A json string returned to WebUI from a "DialogClosed" message. + // A json string returned to WebUI from a "dialogClose" message. std::string dialog_close_retval_; // Whether CloseContents() has been called. diff --git a/chromium/ui/views/controls/webview/webview.cc b/chromium/ui/views/controls/webview/webview.cc index f2ea02f48fb..21de7261231 100644 --- a/chromium/ui/views/controls/webview/webview.cc +++ b/chromium/ui/views/controls/webview/webview.cc @@ -10,10 +10,10 @@ #include "content/public/browser/render_view_host.h" #include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_view.h" #include "ipc/ipc_message.h" -#include "ui/base/accessibility/accessibility_types.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_enums.h" +#include "ui/accessibility/ax_view_state.h" +#include "ui/base/ui_base_switches_util.h" #include "ui/events/event.h" #include "ui/views/accessibility/native_view_accessibility.h" #include "ui/views/controls/native/native_view_host.h" @@ -29,63 +29,51 @@ const char WebView::kViewClassName[] = "WebView"; // WebView, public: WebView::WebView(content::BrowserContext* browser_context) - : wcv_holder_(new NativeViewHost), - web_contents_(NULL), + : holder_(new NativeViewHost()), embed_fullscreen_widget_mode_enabled_(false), is_embedding_fullscreen_widget_(false), browser_context_(browser_context), allow_accelerators_(false) { - AddChildView(wcv_holder_); + AddChildView(holder_); // Takes ownership of |holder_|. NativeViewAccessibility::RegisterWebView(this); } WebView::~WebView() { + SetWebContents(NULL); // Make sure all necessary tear-down takes place. NativeViewAccessibility::UnregisterWebView(this); } content::WebContents* WebView::GetWebContents() { - CreateWebContentsWithSiteInstance(NULL); - return web_contents_; -} - -void WebView::CreateWebContentsWithSiteInstance( - content::SiteInstance* site_instance) { - if (!web_contents_) { - wc_owner_.reset(CreateWebContents(browser_context_, site_instance)); - web_contents_ = wc_owner_.get(); - web_contents_->SetDelegate(this); - AttachWebContents(); + if (!web_contents()) { + wc_owner_.reset(CreateWebContents(browser_context_)); + wc_owner_->SetDelegate(this); + SetWebContents(wc_owner_.get()); } + return web_contents(); } -void WebView::SetWebContents(content::WebContents* web_contents) { - if (web_contents == web_contents_) +void WebView::SetWebContents(content::WebContents* replacement) { + if (replacement == web_contents()) return; DetachWebContents(); - if (wc_owner_ != web_contents) + WebContentsObserver::Observe(replacement); + // web_contents() now returns |replacement| from here onwards. + if (wc_owner_ != replacement) wc_owner_.reset(); - web_contents_ = web_contents; if (embed_fullscreen_widget_mode_enabled_) { is_embedding_fullscreen_widget_ = - web_contents_ && web_contents_->GetFullscreenRenderWidgetHostView(); + web_contents() && web_contents()->GetFullscreenRenderWidgetHostView(); } else { - is_embedding_fullscreen_widget_ = false; + DCHECK(!is_embedding_fullscreen_widget_); } AttachWebContents(); + NotifyMaybeTextInputClientChanged(); } void WebView::SetEmbedFullscreenWidgetMode(bool enable) { - bool should_be_embedded = enable; - if (!embed_fullscreen_widget_mode_enabled_ && enable) { - DCHECK(!is_embedding_fullscreen_widget_); - embed_fullscreen_widget_mode_enabled_ = true; - should_be_embedded = - web_contents_ && web_contents_->GetFullscreenRenderWidgetHostView(); - } else if (embed_fullscreen_widget_mode_enabled_ && !enable) { - embed_fullscreen_widget_mode_enabled_ = false; - } - if (should_be_embedded != is_embedding_fullscreen_widget_) - ReattachForFullscreenChange(should_be_embedded); + DCHECK(!web_contents()) + << "Cannot change mode while a WebContents is attached."; + embed_fullscreen_widget_mode_enabled_ = enable; } void WebView::LoadInitialURL(const GURL& url) { @@ -95,7 +83,7 @@ void WebView::LoadInitialURL(const GURL& url) { } void WebView::SetFastResize(bool fast_resize) { - wcv_holder_->set_fast_resize(fast_resize); + holder_->set_fast_resize(fast_resize); } void WebView::OnWebContentsFocused(content::WebContents* web_contents) { @@ -116,8 +104,68 @@ const char* WebView::GetClassName() const { return kViewClassName; } +ui::TextInputClient* WebView::GetTextInputClient() { + // This function delegates the text input handling to the underlying + // content::RenderWidgetHostView. So when the underlying RWHV is destroyed or + // replaced with another one, we have to notify the FocusManager through + // FocusManager::OnTextInputClientChanged() that the focused TextInputClient + // needs to be updated. + if (switches::IsTextInputFocusManagerEnabled() && + web_contents() && !web_contents()->IsBeingDestroyed()) { + content::RenderWidgetHostView* host_view = + is_embedding_fullscreen_widget_ ? + web_contents()->GetFullscreenRenderWidgetHostView() : + web_contents()->GetRenderWidgetHostView(); + if (host_view) + return host_view->GetTextInputClient(); + } + return NULL; +} + void WebView::OnBoundsChanged(const gfx::Rect& previous_bounds) { - wcv_holder_->SetSize(bounds().size()); + // In most cases, the holder is simply sized to fill this WebView's bounds. + // Only WebContentses that are in fullscreen mode and being screen-captured + // will engage the special layout/sizing behavior. + gfx::Rect holder_bounds(bounds().size()); + if (!embed_fullscreen_widget_mode_enabled_ || + !web_contents() || + web_contents()->GetCapturerCount() == 0 || + web_contents()->GetPreferredSize().IsEmpty() || + !(is_embedding_fullscreen_widget_ || + (web_contents()->GetDelegate() && + web_contents()->GetDelegate()-> + IsFullscreenForTabOrPending(web_contents())))) { + holder_->SetBoundsRect(holder_bounds); + return; + } + + // Size the holder to the capture video resolution and center it. If this + // WebView is not large enough to contain the holder at the preferred size, + // scale down to fit (preserving aspect ratio). + const gfx::Size capture_size = web_contents()->GetPreferredSize(); + if (capture_size.width() <= holder_bounds.width() && + capture_size.height() <= holder_bounds.height()) { + // No scaling, just centering. + holder_bounds.ClampToCenteredSize(capture_size); + } else { + // Scale down, preserving aspect ratio, and center. + // TODO(miu): This is basically media::ComputeLetterboxRegion(), and it + // looks like others have written this code elsewhere. Let's considate + // into a shared function ui/gfx/geometry or around there. + const int64 x = static_cast<int64>(capture_size.width()) * + holder_bounds.height(); + const int64 y = static_cast<int64>(capture_size.height()) * + holder_bounds.width(); + if (y < x) { + holder_bounds.ClampToCenteredSize(gfx::Size( + holder_bounds.width(), static_cast<int>(y / capture_size.width()))); + } else { + holder_bounds.ClampToCenteredSize(gfx::Size( + static_cast<int>(x / capture_size.height()), holder_bounds.height())); + } + } + + holder_->SetBoundsRect(holder_bounds); } void WebView::ViewHierarchyChanged( @@ -135,48 +183,48 @@ bool WebView::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) { // We'll first give the page a chance to process the key events. If it does // not process them, they'll be returned to us and we'll treat them as // accelerators then. - return web_contents_ && !web_contents_->IsCrashed(); + return web_contents() && !web_contents()->IsCrashed(); } bool WebView::IsFocusable() const { // We need to be focusable when our contents is not a view hierarchy, as // clicking on the contents needs to focus us. - return !!web_contents_; + return !!web_contents(); } void WebView::OnFocus() { - if (!web_contents_) + if (!web_contents()) return; if (is_embedding_fullscreen_widget_) { content::RenderWidgetHostView* const current_fs_view = - web_contents_->GetFullscreenRenderWidgetHostView(); + web_contents()->GetFullscreenRenderWidgetHostView(); if (current_fs_view) current_fs_view->Focus(); } else { - web_contents_->GetView()->Focus(); + web_contents()->Focus(); } } void WebView::AboutToRequestFocusFromTabTraversal(bool reverse) { - if (web_contents_) - web_contents_->FocusThroughTabTraversal(reverse); + if (web_contents()) + web_contents()->FocusThroughTabTraversal(reverse); } -void WebView::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_GROUPING; +void WebView::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_GROUP; } gfx::NativeViewAccessible WebView::GetNativeViewAccessible() { - if (web_contents_) { + if (web_contents()) { content::RenderWidgetHostView* host_view = - web_contents_->GetRenderWidgetHostView(); + web_contents()->GetRenderWidgetHostView(); if (host_view) return host_view->GetNativeViewAccessible(); } return View::GetNativeViewAccessible(); } -gfx::Size WebView::GetPreferredSize() { +gfx::Size WebView::GetPreferredSize() const { if (preferred_size_ == gfx::Size()) return View::GetPreferredSize(); else @@ -189,7 +237,7 @@ gfx::Size WebView::GetPreferredSize() { void WebView::WebContentsFocused(content::WebContents* web_contents) { DCHECK(wc_owner_.get()); // The WebView is only the delegate of WebContentses it creates itself. - OnWebContentsFocused(web_contents_); + OnWebContentsFocused(wc_owner_.get()); } bool WebView::EmbedsFullscreenWidget() const { @@ -200,19 +248,16 @@ bool WebView::EmbedsFullscreenWidget() const { //////////////////////////////////////////////////////////////////////////////// // WebView, content::WebContentsObserver implementation: +void WebView::RenderViewDeleted(content::RenderViewHost* render_view_host) { + NotifyMaybeTextInputClientChanged(); +} + void WebView::RenderViewHostChanged(content::RenderViewHost* old_host, content::RenderViewHost* new_host) { FocusManager* const focus_manager = GetFocusManager(); if (focus_manager && focus_manager->GetFocusedView() == this) OnFocus(); -} - -void WebView::WebContentsDestroyed(content::WebContents* web_contents) { - // We watch for destruction of WebContents that we host but do not own. If we - // own a WebContents that is being destroyed, we're doing the destroying, so - // we don't want to recursively tear it down while it's being torn down. - if (!wc_owner_.get()) - SetWebContents(NULL); + NotifyMaybeTextInputClientChanged(); } void WebView::DidShowFullscreenWidget(int routing_id) { @@ -225,21 +270,27 @@ void WebView::DidDestroyFullscreenWidget(int routing_id) { ReattachForFullscreenChange(false); } +void WebView::DidToggleFullscreenModeForTab(bool entered_fullscreen) { + if (embed_fullscreen_widget_mode_enabled_) + ReattachForFullscreenChange(entered_fullscreen); +} + //////////////////////////////////////////////////////////////////////////////// // WebView, private: void WebView::AttachWebContents() { // Prevents attachment if the WebView isn't already in a Widget, or it's // already attached. - if (!GetWidget() || !web_contents_) + if (!GetWidget() || !web_contents()) return; const gfx::NativeView view_to_attach = is_embedding_fullscreen_widget_ ? - web_contents_->GetFullscreenRenderWidgetHostView()->GetNativeView() : - web_contents_->GetView()->GetNativeView(); - if (wcv_holder_->native_view() == view_to_attach) + web_contents()->GetFullscreenRenderWidgetHostView()->GetNativeView() : + web_contents()->GetNativeView(); + OnBoundsChanged(bounds()); + if (holder_->native_view() == view_to_attach) return; - wcv_holder_->Attach(view_to_attach); + holder_->Attach(view_to_attach); // The view will not be focused automatically when it is attached, so we need // to pass on focus to it if the FocusManager thinks the view is focused. Note @@ -248,58 +299,61 @@ void WebView::AttachWebContents() { if (focus_manager && focus_manager->GetFocusedView() == this) OnFocus(); - WebContentsObserver::Observe(web_contents_); - -#if defined(OS_WIN) && defined(USE_AURA) +#if defined(OS_WIN) if (!is_embedding_fullscreen_widget_) { - web_contents_->SetParentNativeViewAccessible( + web_contents()->SetParentNativeViewAccessible( parent()->GetNativeViewAccessible()); } #endif } void WebView::DetachWebContents() { - if (web_contents_) { - wcv_holder_->Detach(); + if (web_contents()) { + holder_->Detach(); #if defined(OS_WIN) - if (!is_embedding_fullscreen_widget_) { -#if !defined(USE_AURA) - // TODO(beng): This should either not be necessary, or be done implicitly - // by NativeViewHostWin on Detach(). As it stands, this is needed so that - // the of the detached contents knows to tell the renderer it's been - // hidden. - // - // Moving this out of here would also mean we wouldn't be potentially - // calling member functions on a half-destroyed WebContents. - ShowWindow(web_contents_->GetView()->GetNativeView(), SW_HIDE); -#else - web_contents_->SetParentNativeViewAccessible(NULL); -#endif - } + if (!is_embedding_fullscreen_widget_) + web_contents()->SetParentNativeViewAccessible(NULL); #endif } - WebContentsObserver::Observe(NULL); } void WebView::ReattachForFullscreenChange(bool enter_fullscreen) { - DetachWebContents(); - is_embedding_fullscreen_widget_ = enter_fullscreen && - web_contents_ && web_contents_->GetFullscreenRenderWidgetHostView(); - AttachWebContents(); + DCHECK(embed_fullscreen_widget_mode_enabled_); + const bool web_contents_has_separate_fs_widget = + web_contents() && web_contents()->GetFullscreenRenderWidgetHostView(); + if (is_embedding_fullscreen_widget_ || web_contents_has_separate_fs_widget) { + // Shutting down or starting up the embedding of the separate fullscreen + // widget. Need to detach and re-attach to a different native view. + DetachWebContents(); + is_embedding_fullscreen_widget_ = + enter_fullscreen && web_contents_has_separate_fs_widget; + AttachWebContents(); + } else { + // Entering or exiting "non-Flash" fullscreen mode, where the native view is + // the same. So, do not change attachment. + OnBoundsChanged(bounds()); + } + NotifyMaybeTextInputClientChanged(); +} + +void WebView::NotifyMaybeTextInputClientChanged() { + // Update the TextInputClient as needed; see GetTextInputClient(). + FocusManager* const focus_manager = GetFocusManager(); + if (focus_manager) + focus_manager->OnTextInputClientChanged(this); } content::WebContents* WebView::CreateWebContents( - content::BrowserContext* browser_context, - content::SiteInstance* site_instance) { + content::BrowserContext* browser_context) { content::WebContents* contents = NULL; if (ViewsDelegate::views_delegate) { contents = ViewsDelegate::views_delegate->CreateWebContents( - browser_context, site_instance); + browser_context, NULL); } if (!contents) { content::WebContents::CreateParams create_params( - browser_context, site_instance); + browser_context, NULL); return content::WebContents::Create(create_params); } diff --git a/chromium/ui/views/controls/webview/webview.gyp b/chromium/ui/views/controls/webview/webview.gyp index 6d74ef2dfad..39d40201d80 100644 --- a/chromium/ui/views/controls/webview/webview.gyp +++ b/chromium/ui/views/controls/webview/webview.gyp @@ -17,9 +17,10 @@ '../../../../content/content.gyp:content_browser', '../../../../skia/skia.gyp:skia', '../../../../url/url.gyp:url_lib', + '../../../base/ui_base.gyp:ui_base', '../../../events/events.gyp:events', '../../../gfx/gfx.gyp:gfx', - '../../../ui.gyp:ui', + '../../../gfx/gfx.gyp:gfx_geometry', '../../../web_dialogs/web_dialogs.gyp:web_dialogs', '../../views.gyp:views', ], diff --git a/chromium/ui/views/controls/webview/webview.h b/chromium/ui/views/controls/webview/webview.h index ae789be2aaa..5cbb8874286 100644 --- a/chromium/ui/views/controls/webview/webview.h +++ b/chromium/ui/views/controls/webview/webview.h @@ -13,14 +13,24 @@ #include "ui/views/controls/webview/webview_export.h" #include "ui/views/view.h" -namespace content { -class SiteInstance; -} - namespace views { class NativeViewHost; +// Provides a view of a WebContents instance. WebView can be used standalone, +// creating and displaying an internally-owned WebContents; or within a full +// browser where the browser swaps its own WebContents instances in/out (e.g., +// for browser tabs). +// +// WebView creates and owns a single child view, a NativeViewHost, which will +// hold and display the native view provided by a WebContents. +// +// EmbedFullscreenWidgetMode: When enabled, WebView will observe for WebContents +// fullscreen changes and automatically swap the normal native view with the +// fullscreen native view (if different). In addition, if the WebContents is +// being screen-captured, the view will be centered within WebView, sized to +// the aspect ratio of the capture video resolution, and scaling will be avoided +// whenever possible. class WEBVIEW_EXPORT WebView : public View, public content::WebContentsDelegate, public content::WebContentsObserver { @@ -34,10 +44,6 @@ class WEBVIEW_EXPORT WebView : public View, // WebView owns this implicitly created WebContents. content::WebContents* GetWebContents(); - // Creates a WebContents if none is yet assocaited with this WebView, with the - // specified site instance. The WebView owns this WebContents. - void CreateWebContentsWithSiteInstance(content::SiteInstance* site_instance); - // WebView does not assume ownership of WebContents set via this method, only // those it implicitly creates via GetWebContents() above. void SetWebContents(content::WebContents* web_contents); @@ -48,7 +54,9 @@ class WEBVIEW_EXPORT WebView : public View, // widget or restore the normal WebContentsView. void SetEmbedFullscreenWidgetMode(bool mode); - content::WebContents* web_contents() { return web_contents_; } + content::WebContents* web_contents() const { + return content::WebContentsObserver::web_contents(); + } content::BrowserContext* browser_context() { return browser_context_; } @@ -86,8 +94,9 @@ class WEBVIEW_EXPORT WebView : public View, // Overridden from View: virtual const char* GetClassName() const OVERRIDE; + virtual ui::TextInputClient* GetTextInputClient() OVERRIDE; - private: + protected: // Overridden from View: virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE; virtual void ViewHierarchyChanged( @@ -97,44 +106,48 @@ class WEBVIEW_EXPORT WebView : public View, virtual bool IsFocusable() const OVERRIDE; virtual void OnFocus() OVERRIDE; virtual void AboutToRequestFocusFromTabTraversal(bool reverse) OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; virtual gfx::NativeViewAccessible GetNativeViewAccessible() OVERRIDE; - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; // Overridden from content::WebContentsDelegate: virtual void WebContentsFocused(content::WebContents* web_contents) OVERRIDE; virtual bool EmbedsFullscreenWidget() const OVERRIDE; // Overridden from content::WebContentsObserver: + virtual void RenderViewDeleted( + content::RenderViewHost* render_view_host) OVERRIDE; virtual void RenderViewHostChanged( content::RenderViewHost* old_host, content::RenderViewHost* new_host) OVERRIDE; - virtual void WebContentsDestroyed( - content::WebContents* web_contents) OVERRIDE; virtual void DidShowFullscreenWidget(int routing_id) OVERRIDE; virtual void DidDestroyFullscreenWidget(int routing_id) OVERRIDE; + virtual void DidToggleFullscreenModeForTab(bool entered_fullscreen) OVERRIDE; // Workaround for MSVC++ linker bug/feature that requires // instantiation of the inline IPC::Listener methods in all translation units. virtual void OnChannelConnected(int32 peer_id) OVERRIDE {} virtual void OnChannelError() OVERRIDE {} + virtual void OnBadMessageReceived(const IPC::Message& message) OVERRIDE {} + private: void AttachWebContents(); void DetachWebContents(); void ReattachForFullscreenChange(bool enter_fullscreen); + void NotifyMaybeTextInputClientChanged(); // Create a regular or test web contents (based on whether we're running // in a unit test or not). content::WebContents* CreateWebContents( - content::BrowserContext* browser_context, - content::SiteInstance* site_instance); + content::BrowserContext* browser_context); - NativeViewHost* wcv_holder_; + NativeViewHost* const holder_; + // Non-NULL if |web_contents()| was created and is owned by this WebView. scoped_ptr<content::WebContents> wc_owner_; - content::WebContents* web_contents_; // When true, WebView auto-embeds fullscreen widgets as a child view. bool embed_fullscreen_widget_mode_enabled_; // Set to true while WebView is embedding a fullscreen widget view as a child - // view instead of the normal WebContentsView render view. + // view instead of the normal WebContentsView render view. Note: This will be + // false in the case of non-Flash fullscreen. bool is_embedding_fullscreen_widget_; content::BrowserContext* browser_context_; bool allow_accelerators_; diff --git a/chromium/ui/views/controls/webview/webview_interactive_uitest.cc b/chromium/ui/views/controls/webview/webview_interactive_uitest.cc new file mode 100644 index 00000000000..f1179b83eab --- /dev/null +++ b/chromium/ui/views/controls/webview/webview_interactive_uitest.cc @@ -0,0 +1,82 @@ +// Copyright 2014 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/controls/webview/webview.h" + +#include "base/command_line.h" +#include "base/memory/scoped_ptr.h" +#include "content/public/browser/web_contents.h" +#include "content/public/test/test_browser_context.h" +#include "content/public/test/test_browser_thread.h" +#include "ui/base/ime/text_input_focus_manager.h" +#include "ui/base/ui_base_switches.h" +#include "ui/gl/gl_surface.h" +#include "ui/views/test/webview_test_helper.h" +#include "ui/views/test/widget_test.h" + +namespace { + +class WebViewInteractiveUiTest : public views::test::WidgetTest { + public: + WebViewInteractiveUiTest() + : ui_thread_(content::BrowserThread::UI, base::MessageLoop::current()) {} + + virtual void SetUp() OVERRIDE { + gfx::GLSurface::InitializeOneOffForTests(); + WidgetTest::SetUp(); + } + + protected: + content::BrowserContext* browser_context() { return &browser_context_; } + + private: + content::TestBrowserContext browser_context_; + views::WebViewTestHelper webview_test_helper_; + content::TestBrowserThread ui_thread_; + + DISALLOW_COPY_AND_ASSIGN(WebViewInteractiveUiTest); +}; + +TEST_F(WebViewInteractiveUiTest, TextInputClientIsUpToDate) { + // WebViewInteractiveUiTest.TextInputClientIsUpToDate needs + // kEnableTextInputFocusManager flag to be enabled. + base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); + cmd_line->AppendSwitch(switches::kEnableTextInputFocusManager); + + // Create a top level widget and a webview as its content. + views::Widget* widget = CreateTopLevelFramelessPlatformWidget(); + views::WebView* webview = new views::WebView(browser_context()); + widget->SetContentsView(webview); + widget->Show(); + webview->RequestFocus(); + + ui::TextInputFocusManager* text_input_focus_manager = + ui::TextInputFocusManager::GetInstance(); + const ui::TextInputClient* null_text_input_client = NULL; + EXPECT_EQ(null_text_input_client, webview->GetTextInputClient()); + EXPECT_EQ(text_input_focus_manager->GetFocusedTextInputClient(), + webview->GetTextInputClient()); + + // Case 1: Creates a new WebContents. + webview->GetWebContents(); + webview->RequestFocus(); + ui::TextInputClient* client1 = webview->GetTextInputClient(); + EXPECT_NE(null_text_input_client, client1); + EXPECT_EQ(text_input_focus_manager->GetFocusedTextInputClient(), client1); + + // Case 2: Replaces a WebContents by SetWebContents(). + content::WebContents::CreateParams params(browser_context()); + scoped_ptr<content::WebContents> web_contents( + content::WebContents::Create(params)); + webview->SetWebContents(web_contents.get()); + ui::TextInputClient* client2 = webview->GetTextInputClient(); + EXPECT_NE(null_text_input_client, client2); + EXPECT_EQ(text_input_focus_manager->GetFocusedTextInputClient(), client2); + EXPECT_NE(client1, client2); + + widget->Close(); + RunPendingMessages(); +} + +} // namespace diff --git a/chromium/ui/views/controls/webview/webview_tests.gyp b/chromium/ui/views/controls/webview/webview_tests.gyp new file mode 100644 index 00000000000..0adc5c2befc --- /dev/null +++ b/chromium/ui/views/controls/webview/webview_tests.gyp @@ -0,0 +1,38 @@ +# Copyright 2014 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. + +{ + 'variables': { + 'chromium_code': 1, + }, + 'targets': [ + { + 'target_name': 'webview_test_support', + 'type': 'static_library', + 'dependencies': [ + '../../../../base/base.gyp:base', + '../../../../content/content.gyp:content', + '../../../../content/content_shell_and_tests.gyp:test_support_content', + '../../../../ipc/ipc.gyp:test_support_ipc', + '../../../../skia/skia.gyp:skia', + '../../../../testing/gtest.gyp:gtest', + '../../../aura/aura.gyp:aura', + '../../../base/ui_base.gyp:ui_base', + '../../../events/events.gyp:events', + '../../../gfx/gfx.gyp:gfx', + '../../../gfx/gfx.gyp:gfx_geometry', + '../../views.gyp:views', + '../../views.gyp:views_test_support', + 'webview.gyp:webview', + ], + 'include_dirs': [ + '../../../..', + ], + 'sources': [ + '../../test/webview_test_helper.cc', + '../../test/webview_test_helper.h', + ], + }, + ], +} diff --git a/chromium/ui/views/corewm/DEPS b/chromium/ui/views/corewm/DEPS index 06abf634093..a2d52d61d09 100644 --- a/chromium/ui/views/corewm/DEPS +++ b/chromium/ui/views/corewm/DEPS @@ -1,4 +1,57 @@ -# This code should not depend on Views.
-
-"-ui/views",
-"+ui/views/views_export.h"
+# This code should not depend on Views. + +include_rules = [ + "-ui/views", + "+ui/views/corewm", + "+ui/views/views_export.h", +] + +# TODO: temporary, don't add more. +specific_include_rules = { + "tooltip_controller.cc": [ + "+ui/views/widget/tooltip_manager.h", + ], + + "tooltip_aura.cc": [ + "+ui/views/background.h", + "+ui/views/border.h", + "+ui/views/widget/widget.h", + ], + + "desktop_capture_controller_unittest.cc": [ + "+ui/views/test/views_test_base.h", + "+ui/views/view.h", + "+ui/views/widget/desktop_aura/desktop_native_widget_aura.h", + "+ui/views/widget/desktop_aura/desktop_screen_position_client.h", + "+ui/views/widget/root_view.h", + "+ui/views/widget/widget.h", + ], + + "tooltip_controller_unittest.cc": [ + "+ui/views/test/desktop_test_views_delegate.h", + "+ui/views/test/test_views_delegate.h", + "+ui/views/view.h", + "+ui/views/widget/desktop_aura/desktop_native_widget_aura.h", + "+ui/views/widget/desktop_aura/desktop_screen.h", + "+ui/views/widget/tooltip_manager.h", + "+ui/views/widget/widget.h", + ], + + "tooltip_aura.h": [ + "+ui/views/controls/label.h", + "+ui/views/widget/widget_observer.h", + ], + + "tooltip_controller_test_helper.h": [ + "+ui/views/view.h", + ], + + "capture_controller_unittest.cc": [ + "+ui/views/test/views_test_base.h", + "+ui/views/view.h", + "+ui/views/widget/desktop_aura/desktop_native_widget_aura.h", + "+ui/views/widget/desktop_aura/desktop_screen_position_client.h", + "+ui/views/widget/root_view.h", + "+ui/views/widget/widget.h", + ], +} diff --git a/chromium/ui/views/corewm/base_focus_rules.cc b/chromium/ui/views/corewm/base_focus_rules.cc deleted file mode 100644 index 9520c749cd0..00000000000 --- a/chromium/ui/views/corewm/base_focus_rules.cc +++ /dev/null @@ -1,196 +0,0 @@ -// 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/corewm/base_focus_rules.h" - -#include "ui/aura/client/activation_delegate.h" -#include "ui/aura/client/focus_client.h" -#include "ui/aura/root_window.h" -#include "ui/aura/window.h" -#include "ui/views/corewm/window_modality_controller.h" - -namespace views { -namespace corewm { -namespace { - -aura::Window* GetFocusedWindow(aura::Window* context) { - aura::client::FocusClient* focus_client = - aura::client::GetFocusClient(context); - return focus_client ? focus_client->GetFocusedWindow() : NULL; -} - -} // namespace - -//////////////////////////////////////////////////////////////////////////////// -// BaseFocusRules, protected: - -BaseFocusRules::BaseFocusRules() { -} - -BaseFocusRules::~BaseFocusRules() { -} - -bool BaseFocusRules::IsWindowConsideredVisibleForActivation( - aura::Window* window) const { - return window->IsVisible(); -} - -//////////////////////////////////////////////////////////////////////////////// -// BaseFocusRules, FocusRules implementation: - -bool BaseFocusRules::IsToplevelWindow(aura::Window* window) const { - // The window must in a valid hierarchy. - if (!window->GetRootWindow()) - return false; - - // The window must exist within a container that supports activation. - // The window cannot be blocked by a modal transient. - return SupportsChildActivation(window->parent()); -} - -bool BaseFocusRules::CanActivateWindow(aura::Window* window) const { - // It is possible to activate a NULL window, it is equivalent to clearing - // activation. - if (!window) - return true; - - // Only toplevel windows can be activated. - if (!IsToplevelWindow(window)) - return false; - - // The window must be visible. - if (!IsWindowConsideredVisibleForActivation(window)) - return false; - - // The window's activation delegate must allow this window to be activated. - if (aura::client::GetActivationDelegate(window) && - !aura::client::GetActivationDelegate(window)->ShouldActivate()) { - return false; - } - - // A window must be focusable to be activatable. We don't call - // CanFocusWindow() from here because it will call back to us via - // GetActivatableWindow(). - if (!window->CanFocus()) - return false; - - // The window cannot be blocked by a modal transient. - return !GetModalTransient(window); -} - -bool BaseFocusRules::CanFocusWindow(aura::Window* window) const { - // It is possible to focus a NULL window, it is equivalent to clearing focus. - if (!window) - return true; - - // The focused window is always inside the active window, so windows that - // aren't activatable can't contain the focused window. - aura::Window* activatable = GetActivatableWindow(window); - if (!activatable || !activatable->Contains(window)) - return false; - return window->CanFocus(); -} - -aura::Window* BaseFocusRules::GetToplevelWindow(aura::Window* window) const { - aura::Window* parent = window->parent(); - aura::Window* child = window; - while (parent) { - if (IsToplevelWindow(child)) - return child; - - parent = parent->parent(); - child = child->parent(); - } - return NULL; -} - -aura::Window* BaseFocusRules::GetActivatableWindow(aura::Window* window) const { - aura::Window* parent = window->parent(); - aura::Window* child = window; - while (parent) { - if (CanActivateWindow(child)) - return child; - - // CanActivateWindow() above will return false if |child| is blocked by a - // modal transient. In this case the modal is or contains the activatable - // window. We recurse because the modal may itself be blocked by a modal - // transient. - aura::Window* modal_transient = GetModalTransient(child); - if (modal_transient) - return GetActivatableWindow(modal_transient); - - if (child->transient_parent()) { - // To avoid infinite recursion, if |child| has a transient parent - // whose own modal transient is |child| itself, just return |child|. - aura::Window* parent_modal_transient = - GetModalTransient(child->transient_parent()); - if (parent_modal_transient == child) - return child; - - return GetActivatableWindow(child->transient_parent()); - } - - parent = parent->parent(); - child = child->parent(); - } - return NULL; -} - -aura::Window* BaseFocusRules::GetFocusableWindow(aura::Window* window) const { - if (CanFocusWindow(window)) - return window; - - // |window| may be in a hierarchy that is non-activatable, in which case we - // need to cut over to the activatable hierarchy. - aura::Window* activatable = GetActivatableWindow(window); - if (!activatable) { - // There may not be a related activatable hierarchy to cut over to, in which - // case we try an unrelated one. - aura::Window* toplevel = GetToplevelWindow(window); - if (toplevel) - activatable = GetNextActivatableWindow(toplevel); - if (!activatable) - return NULL; - } - - if (!activatable->Contains(window)) { - // If there's already a child window focused in the activatable hierarchy, - // just use that (i.e. don't shift focus), otherwise we need to at least cut - // over to the activatable hierarchy. - aura::Window* focused = GetFocusedWindow(activatable); - return activatable->Contains(focused) ? focused : activatable; - } - - while (window && !CanFocusWindow(window)) - window = window->parent(); - return window; -} - -aura::Window* BaseFocusRules::GetNextActivatableWindow( - aura::Window* ignore) const { - DCHECK(ignore); - - // Can be called from the RootWindow's destruction, which has a NULL parent. - if (!ignore->parent()) - return NULL; - - // In the basic scenarios handled by BasicFocusRules, the pool of activatable - // windows is limited to the |ignore|'s siblings. - const aura::Window::Windows& siblings = ignore->parent()->children(); - DCHECK(!siblings.empty()); - - for (aura::Window::Windows::const_reverse_iterator rit = siblings.rbegin(); - rit != siblings.rend(); - ++rit) { - aura::Window* cur = *rit; - if (cur == ignore) - continue; - if (CanActivateWindow(cur)) - return cur; - } - return NULL; -} - -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/base_focus_rules.h b/chromium/ui/views/corewm/base_focus_rules.h deleted file mode 100644 index 4c564b0e798..00000000000 --- a/chromium/ui/views/corewm/base_focus_rules.h +++ /dev/null @@ -1,47 +0,0 @@ -// 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_COREWM_BASE_FOCUS_RULES_H_ -#define UI_VIEWS_COREWM_BASE_FOCUS_RULES_H_ - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "ui/views/corewm/focus_rules.h" - -namespace views { -namespace corewm { - -// A set of basic focus and activation rules. Specializations should most likely -// subclass this and call up to these methods rather than reimplementing them. -class VIEWS_EXPORT BaseFocusRules : public FocusRules { - protected: - BaseFocusRules(); - virtual ~BaseFocusRules(); - - // Returns true if the children of |window| can be activated. - virtual bool SupportsChildActivation(aura::Window* window) const = 0; - - // Returns true if |window| is considered visible for activation purposes. - virtual bool IsWindowConsideredVisibleForActivation( - aura::Window* window) const; - - // Overridden from FocusRules: - virtual bool IsToplevelWindow(aura::Window* window) const OVERRIDE; - virtual bool CanActivateWindow(aura::Window* window) const OVERRIDE; - virtual bool CanFocusWindow(aura::Window* window) const OVERRIDE; - virtual aura::Window* GetToplevelWindow(aura::Window* window) const OVERRIDE; - virtual aura::Window* GetActivatableWindow( - aura::Window* window) const OVERRIDE; - virtual aura::Window* GetFocusableWindow(aura::Window* window) const OVERRIDE; - virtual aura::Window* GetNextActivatableWindow( - aura::Window* ignore) const OVERRIDE; - - private: - DISALLOW_COPY_AND_ASSIGN(BaseFocusRules); -}; - -} // namespace corewm -} // namespace views - -#endif // UI_VIEWS_COREWM_BASE_FOCUS_RULES_H_ diff --git a/chromium/ui/views/corewm/capture_controller.cc b/chromium/ui/views/corewm/capture_controller.cc deleted file mode 100644 index 9a65e381da1..00000000000 --- a/chromium/ui/views/corewm/capture_controller.cc +++ /dev/null @@ -1,145 +0,0 @@ -// 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/corewm/capture_controller.h" - -#include "ui/aura/root_window.h" -#include "ui/aura/window.h" - -namespace views { -namespace corewm { - -//////////////////////////////////////////////////////////////////////////////// -// CaptureController, public: - -void CaptureController::Attach(aura::Window* root) { - DCHECK_EQ(0u, root_windows_.count(root)); - root_windows_.insert(root); - aura::client::SetCaptureClient(root, this); -} - -void CaptureController::Detach(aura::Window* root) { - root_windows_.erase(root); - aura::client::SetCaptureClient(root, NULL); -} - -//////////////////////////////////////////////////////////////////////////////// -// CaptureController, aura::client::CaptureClient implementation: - -void CaptureController::SetCapture(aura::Window* new_capture_window) { - if (capture_window_ == new_capture_window) - return; - - // Make sure window has a root window. - DCHECK(!new_capture_window || new_capture_window->GetRootWindow()); - DCHECK(!capture_window_ || capture_window_->GetRootWindow()); - - aura::Window* old_capture_window = capture_window_; - aura::Window* old_capture_root = old_capture_window ? - old_capture_window->GetRootWindow() : NULL; - - // Copy the list in case it's modified out from under us. - RootWindows root_windows(root_windows_); - - // 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. - if (new_capture_window) { - ui::GestureRecognizer::Get()->TransferEventsTo(old_capture_window, - new_capture_window); - } - - capture_window_ = new_capture_window; - - for (RootWindows::const_iterator i = root_windows.begin(); - i != root_windows.end(); ++i) { - aura::client::CaptureDelegate* delegate = (*i)->GetDispatcher(); - delegate->UpdateCapture(old_capture_window, new_capture_window); - } - - aura::Window* capture_root = - capture_window_ ? capture_window_->GetRootWindow() : NULL; - if (capture_root != old_capture_root) { - if (old_capture_root) { - aura::client::CaptureDelegate* delegate = - old_capture_root->GetDispatcher(); - delegate->ReleaseNativeCapture(); - } - if (capture_root) { - aura::client::CaptureDelegate* delegate = capture_root->GetDispatcher(); - delegate->SetNativeCapture(); - } - } -} - -void CaptureController::ReleaseCapture(aura::Window* window) { - if (capture_window_ != window) - return; - SetCapture(NULL); -} - -aura::Window* CaptureController::GetCaptureWindow() { - return capture_window_; -} - -aura::Window* CaptureController::GetGlobalCaptureWindow() { - return capture_window_; -} - -//////////////////////////////////////////////////////////////////////////////// -// CaptureController, private: - -CaptureController::CaptureController() - : capture_window_(NULL) { -} - -CaptureController::~CaptureController() { -} - -//////////////////////////////////////////////////////////////////////////////// -// ScopedCaptureClient: - -// static -CaptureController* ScopedCaptureClient::capture_controller_ = NULL; - -ScopedCaptureClient::ScopedCaptureClient(aura::Window* root) - : root_window_(root) { - root->AddObserver(this); - if (!capture_controller_) - capture_controller_ = new CaptureController; - capture_controller_->Attach(root); -} - -ScopedCaptureClient::~ScopedCaptureClient() { - Shutdown(); -} - -// static -bool ScopedCaptureClient::IsActive() { - return capture_controller_ && capture_controller_->is_active(); -} - -void ScopedCaptureClient::OnWindowDestroyed(aura::Window* window) { - DCHECK_EQ(window, root_window_); - Shutdown(); -} - -void ScopedCaptureClient::Shutdown() { - if (!root_window_) - return; - - root_window_->RemoveObserver(this); - capture_controller_->Detach(root_window_); - if (!capture_controller_->is_active()) { - delete capture_controller_; - capture_controller_ = NULL; - } - root_window_ = NULL; -} - -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/capture_controller.h b/chromium/ui/views/corewm/capture_controller.h deleted file mode 100644 index e7d1ffd6eca..00000000000 --- a/chromium/ui/views/corewm/capture_controller.h +++ /dev/null @@ -1,88 +0,0 @@ -// 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_COREWM_CAPTURE_CONTROLLER_H_ -#define UI_VIEWS_COREWM_CAPTURE_CONTROLLER_H_ - -#include <set> - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "ui/aura/client/capture_client.h" -#include "ui/aura/window_observer.h" -#include "ui/views/views_export.h" - -namespace views { -namespace corewm { - -// Internal CaptureClient implementation. See ScopedCaptureClient for details. -class VIEWS_EXPORT CaptureController : public aura::client::CaptureClient { - public: - // Adds |root| to the list of RootWindows notified when capture changes. - void Attach(aura::Window* root); - - // Removes |root| from the list of RootWindows notified when capture changes. - void Detach(aura::Window* root); - - // Returns true if this CaptureController is installed on at least one - // RootWindow. - bool is_active() const { return !root_windows_.empty(); } - - // Overridden from aura::client::CaptureClient: - virtual void SetCapture(aura::Window* window) OVERRIDE; - virtual void ReleaseCapture(aura::Window* window) OVERRIDE; - virtual aura::Window* GetCaptureWindow() OVERRIDE; - virtual aura::Window* GetGlobalCaptureWindow() OVERRIDE; - - private: - friend class ScopedCaptureClient; - typedef std::set<aura::Window*> RootWindows; - - CaptureController(); - virtual ~CaptureController(); - - // The current capture window. NULL if there is no capture window. - aura::Window* capture_window_; - - // Set of RootWindows notified when capture changes. - RootWindows root_windows_; - - DISALLOW_COPY_AND_ASSIGN(CaptureController); -}; - -// ScopedCaptureClient is responsible for creating a CaptureClient for a -// RootWindow. Specifically it creates a single CaptureController that is shared -// among all ScopedCaptureClients and adds the RootWindow to it. -class VIEWS_EXPORT ScopedCaptureClient : public aura::WindowObserver { - public: - explicit ScopedCaptureClient(aura::Window* root); - virtual ~ScopedCaptureClient(); - - // Returns true if there is a CaptureController with at least one RootWindow. - static bool IsActive(); - - aura::client::CaptureClient* capture_client() { - return capture_controller_; - } - - // Overridden from aura::WindowObserver: - virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE; - - private: - // Invoked from destructor and OnWindowDestroyed() to cleanup. - void Shutdown(); - - // The single CaptureController instance. - static CaptureController* capture_controller_; - - // RootWindow this ScopedCaptureClient was create for. - aura::Window* root_window_; - - DISALLOW_COPY_AND_ASSIGN(ScopedCaptureClient); -}; - -} // namespace corewm -} // namespace views - -#endif // UI_VIEWS_COREWM_CAPTURE_CONTROLLER_H_ diff --git a/chromium/ui/views/corewm/capture_controller_unittest.cc b/chromium/ui/views/corewm/capture_controller_unittest.cc index 558a4030f51..ffece4a89ab 100644 --- a/chromium/ui/views/corewm/capture_controller_unittest.cc +++ b/chromium/ui/views/corewm/capture_controller_unittest.cc @@ -2,16 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/views/corewm/capture_controller.h" +#include "ui/wm/core/capture_controller.h" #include "base/logging.h" #include "ui/aura/env.h" -#include "ui/aura/root_window.h" #include "ui/aura/test/aura_test_base.h" #include "ui/aura/test/event_generator.h" #include "ui/aura/test/test_screen.h" #include "ui/aura/test/test_window_delegate.h" #include "ui/aura/window.h" +#include "ui/aura/window_event_dispatcher.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" #include "ui/views/test/views_test_base.h" @@ -32,25 +32,21 @@ class CaptureControllerTest : public aura::test::AuraTestBase { virtual void SetUp() OVERRIDE { AuraTestBase::SetUp(); - capture_controller_.reset(new corewm::ScopedCaptureClient(root_window())); + capture_controller_.reset(new wm::ScopedCaptureClient(root_window())); - second_root_.reset(new aura::RootWindow( - aura::RootWindow::CreateParams(gfx::Rect(0, 0, 800, 600)))); - second_root_->Init(); - second_root_->window()->Show(); - second_root_->SetHostSize(gfx::Size(800, 600)); + second_host_.reset(aura::WindowTreeHost::Create(gfx::Rect(0, 0, 800, 600))); + second_host_->InitHost(); + second_host_->window()->Show(); + second_host_->SetBounds(gfx::Rect(800, 600)); second_capture_controller_.reset( - new corewm::ScopedCaptureClient(second_root_->window())); + new wm::ScopedCaptureClient(second_host_->window())); #if !defined(OS_CHROMEOS) - 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_->window(), - second_desktop_position_client_.get()); + desktop_position_client_.reset( + new DesktopScreenPositionClient(root_window())); + + second_desktop_position_client_.reset( + new DesktopScreenPositionClient(second_host_->window())); #endif } @@ -63,7 +59,7 @@ class CaptureControllerTest : public aura::test::AuraTestBase { second_capture_controller_.reset(); // Kill any active compositors before we hit the compositor shutdown paths. - second_root_.reset(); + second_host_.reset(); #if !defined(OS_CHROMEOS) desktop_position_client_.reset(); @@ -81,9 +77,9 @@ class CaptureControllerTest : public aura::test::AuraTestBase { return second_capture_controller_->capture_client()->GetCaptureWindow(); } - scoped_ptr<corewm::ScopedCaptureClient> capture_controller_; - scoped_ptr<aura::RootWindow> second_root_; - scoped_ptr<corewm::ScopedCaptureClient> second_capture_controller_; + scoped_ptr<wm::ScopedCaptureClient> capture_controller_; + scoped_ptr<aura::WindowTreeHost> second_host_; + scoped_ptr<wm::ScopedCaptureClient> second_capture_controller_; #if !defined(OS_CHROMEOS) scoped_ptr<aura::client::ScreenPositionClient> desktop_position_client_; scoped_ptr<aura::client::ScreenPositionClient> @@ -96,50 +92,50 @@ class CaptureControllerTest : public aura::test::AuraTestBase { // 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(CaptureControllerTest, ResetMouseEventHandlerOnCapture) { - // Create a window inside the RootWindow. + // Create a window inside the WindowEventDispatcher. 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|. + // Make a synthesized mouse down event. Ensure that the WindowEventDispatcher + // 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); - dispatcher()->AsRootWindowHostDelegate()->OnHostMouseEvent( - &mouse_pressed_event); - EXPECT_EQ(w1.get(), dispatcher()->mouse_pressed_handler()); + gfx::Point(5, 5), 0, 0); + DispatchEventUsingWindowDispatcher(&mouse_pressed_event); + EXPECT_EQ(w1.get(), host()->dispatcher()->mouse_pressed_handler()); - // Build a window in the second RootWindow. + // Build a window in the second WindowEventDispatcher. scoped_ptr<aura::Window> w2( - CreateNormalWindow(2, second_root_->window(), NULL)); + CreateNormalWindow(2, second_host_->window(), NULL)); // The act of having the second window take capture should clear out mouse - // pressed handler in the first RootWindow. + // pressed handler in the first WindowEventDispatcher. w2->SetCapture(); - EXPECT_EQ(NULL, dispatcher()->mouse_pressed_handler()); + EXPECT_EQ(NULL, host()->dispatcher()->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(CaptureControllerTest, ResetOtherWindowCaptureOnCapture) { - // Create a window inside the RootWindow. + // Create a window inside the WindowEventDispatcher. 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(), GetCaptureWindow()); EXPECT_EQ(w1.get(), GetSecondCaptureWindow()); - // Build a window in the second RootWindow and give it capture. Both capture - // clients should return the same capture window. + // Build a window in the second WindowEventDispatcher and give it capture. + // Both capture clients should return the same capture window. scoped_ptr<aura::Window> w2( - CreateNormalWindow(2, second_root_->window(), NULL)); + CreateNormalWindow(2, second_host_->window(), NULL)); w2->SetCapture(); EXPECT_EQ(w2.get(), GetCaptureWindow()); EXPECT_EQ(w2.get(), GetSecondCaptureWindow()); } -// Verifies the touch target for the RootWindow gets reset on releasing capture. +// Verifies the touch target for the WindowEventDispatcher gets reset on +// releasing capture. TEST_F(CaptureControllerTest, TouchTargetResetOnCaptureChange) { - // Create a window inside the RootWindow. + // Create a window inside the WindowEventDispatcher. scoped_ptr<aura::Window> w1(CreateNormalWindow(1, root_window(), NULL)); aura::test::EventGenerator event_generator1(root_window()); event_generator1.PressTouch(); @@ -148,17 +144,17 @@ TEST_F(CaptureControllerTest, TouchTargetResetOnCaptureChange) { EXPECT_EQ(w1.get(), GetCaptureWindow()); EXPECT_EQ(w1.get(), GetSecondCaptureWindow()); - // Build a window in the second RootWindow and give it capture. Both capture - // clients should return the same capture window. + // Build a window in the second WindowEventDispatcher and give it capture. + // Both capture clients should return the same capture window. scoped_ptr<aura::Window> w2( - CreateNormalWindow(2, second_root_->window(), NULL)); + CreateNormalWindow(2, second_host_->window(), NULL)); w2->SetCapture(); EXPECT_EQ(w2.get(), GetCaptureWindow()); EXPECT_EQ(w2.get(), GetSecondCaptureWindow()); // Release capture on the window. Releasing capture should reset the touch - // target of the first RootWindow (as it no longer contains the capture - // target). + // target of the first WindowEventDispatcher (as it no longer contains the + // capture target). w2->ReleaseCapture(); EXPECT_EQ(static_cast<aura::Window*>(NULL), GetCaptureWindow()); EXPECT_EQ(static_cast<aura::Window*>(NULL), GetSecondCaptureWindow()); diff --git a/chromium/ui/views/corewm/compound_event_filter.cc b/chromium/ui/views/corewm/compound_event_filter.cc deleted file mode 100644 index efeca5cad2a..00000000000 --- a/chromium/ui/views/corewm/compound_event_filter.cc +++ /dev/null @@ -1,279 +0,0 @@ -// 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/corewm/compound_event_filter.h" - -#include "base/containers/hash_tables.h" -#include "base/logging.h" -#include "ui/aura/client/activation_client.h" -#include "ui/aura/client/cursor_client.h" -#include "ui/aura/client/drag_drop_client.h" -#include "ui/aura/env.h" -#include "ui/aura/root_window.h" -#include "ui/aura/window.h" -#include "ui/aura/window_delegate.h" -#include "ui/aura/window_tracker.h" -#include "ui/base/hit_test.h" -#include "ui/events/event.h" - -namespace views { -namespace corewm { - -namespace { - -bool ShouldHideCursorOnKeyEvent(const ui::KeyEvent& event) { -#if defined(OS_CHROMEOS) - // All alt and control key commands are ignored. - if (event.IsAltDown() || event.IsControlDown()) - return false; - - static bool inited = false; - static base::hash_set<int32> ignored_keys; - if (!inited) { - // Modifiers. - ignored_keys.insert(ui::VKEY_SHIFT); - ignored_keys.insert(ui::VKEY_CONTROL); - ignored_keys.insert(ui::VKEY_MENU); - - // Search key == VKEY_LWIN. - ignored_keys.insert(ui::VKEY_LWIN); - - // Function keys. - for (int key = ui::VKEY_F1; key <= ui::VKEY_F24; ++key) - ignored_keys.insert(key); - - // Media keys. - for (int key = ui::VKEY_BROWSER_BACK; key <= ui::VKEY_MEDIA_LAUNCH_APP2; - ++key) { - ignored_keys.insert(key); - } - -#if defined(OS_POSIX) - ignored_keys.insert(ui::VKEY_WLAN); - ignored_keys.insert(ui::VKEY_POWER); - ignored_keys.insert(ui::VKEY_BRIGHTNESS_DOWN); - ignored_keys.insert(ui::VKEY_BRIGHTNESS_UP); - ignored_keys.insert(ui::VKEY_KBD_BRIGHTNESS_DOWN); - ignored_keys.insert(ui::VKEY_KBD_BRIGHTNESS_UP); -#endif - - inited = true; - } - - if (ignored_keys.count(event.key_code()) > 0) - return false; - - return true; -#else // !defined(OS_CHROMEOS) - return false; -#endif // defined(OS_CHROMEOS) -} - -// Returns true if the cursor should be hidden on touch events. -bool ShouldHideCursorOnTouch() { -#if defined(OS_CHROMEOS) - return true; -#else - // Not necessary on windows as windows does it for us. If we do need this - // funcionality on linux (non-chromeos) we need to make sure - // CompoundEventFilter shows on the right root (it currently doesn't always). - return false; -#endif -} - -} // namespace - -//////////////////////////////////////////////////////////////////////////////// -// CompoundEventFilter, public: - -CompoundEventFilter::CompoundEventFilter() { -} - -CompoundEventFilter::~CompoundEventFilter() { - // Additional filters are not owned by CompoundEventFilter and they - // should all be removed when running here. |handlers_| has - // check_empty == true and will DCHECK failure if it is not empty. -} - -// static -gfx::NativeCursor CompoundEventFilter::CursorForWindowComponent( - int window_component) { - switch (window_component) { - case HTBOTTOM: - return ui::kCursorSouthResize; - case HTBOTTOMLEFT: - return ui::kCursorSouthWestResize; - case HTBOTTOMRIGHT: - return ui::kCursorSouthEastResize; - case HTLEFT: - return ui::kCursorWestResize; - case HTRIGHT: - return ui::kCursorEastResize; - case HTTOP: - return ui::kCursorNorthResize; - case HTTOPLEFT: - return ui::kCursorNorthWestResize; - case HTTOPRIGHT: - return ui::kCursorNorthEastResize; - default: - return ui::kCursorNull; - } -} - -void CompoundEventFilter::AddHandler(ui::EventHandler* handler) { - handlers_.AddObserver(handler); -} - -void CompoundEventFilter::RemoveHandler(ui::EventHandler* handler) { - handlers_.RemoveObserver(handler); -} - -//////////////////////////////////////////////////////////////////////////////// -// CompoundEventFilter, private: - -void CompoundEventFilter::UpdateCursor(aura::Window* target, - ui::MouseEvent* event) { - // If drag and drop is in progress, let the drag drop client set the cursor - // instead of setting the cursor here. - aura::Window* root_window = target->GetRootWindow(); - aura::client::DragDropClient* drag_drop_client = - aura::client::GetDragDropClient(root_window); - if (drag_drop_client && drag_drop_client->IsDragDropInProgress()) - return; - - aura::client::CursorClient* cursor_client = - aura::client::GetCursorClient(root_window); - if (cursor_client) { - gfx::NativeCursor cursor = target->GetCursor(event->location()); - if (event->flags() & ui::EF_IS_NON_CLIENT) { - int window_component = - target->delegate()->GetNonClientComponent(event->location()); - cursor = CursorForWindowComponent(window_component); - } - - cursor_client->SetCursor(cursor); - } -} - -void CompoundEventFilter::FilterKeyEvent(ui::KeyEvent* event) { - if (handlers_.might_have_observers()) { - ObserverListBase<ui::EventHandler>::Iterator it(handlers_); - ui::EventHandler* handler; - while (!event->stopped_propagation() && (handler = it.GetNext()) != NULL) - handler->OnKeyEvent(event); - } -} - -void CompoundEventFilter::FilterMouseEvent(ui::MouseEvent* event) { - if (handlers_.might_have_observers()) { - ObserverListBase<ui::EventHandler>::Iterator it(handlers_); - ui::EventHandler* handler; - while (!event->stopped_propagation() && (handler = it.GetNext()) != NULL) - handler->OnMouseEvent(event); - } -} - -void CompoundEventFilter::FilterTouchEvent(ui::TouchEvent* event) { - if (handlers_.might_have_observers()) { - ObserverListBase<ui::EventHandler>::Iterator it(handlers_); - ui::EventHandler* handler; - while (!event->stopped_propagation() && (handler = it.GetNext()) != NULL) - handler->OnTouchEvent(event); - } -} - -void CompoundEventFilter::SetCursorVisibilityOnEvent(aura::Window* target, - ui::Event* event, - bool show) { - if (event->flags() & ui::EF_IS_SYNTHESIZED) - return; - - aura::client::CursorClient* client = - aura::client::GetCursorClient(target->GetRootWindow()); - if (!client) - return; - - if (show) - client->ShowCursor(); - else - client->HideCursor(); -} - -void CompoundEventFilter::SetMouseEventsEnableStateOnEvent(aura::Window* target, - ui::Event* event, - bool enable) { - if (event->flags() & ui::EF_IS_SYNTHESIZED) - return; - aura::client::CursorClient* client = - aura::client::GetCursorClient(target->GetRootWindow()); - if (!client) - return; - - if (enable) - client->EnableMouseEvents(); - else - client->DisableMouseEvents(); -} - -//////////////////////////////////////////////////////////////////////////////// -// CompoundEventFilter, ui::EventHandler implementation: - -void CompoundEventFilter::OnKeyEvent(ui::KeyEvent* event) { - if (ShouldHideCursorOnKeyEvent(*event)) { - SetCursorVisibilityOnEvent( - static_cast<aura::Window*>(event->target()), event, false); - } - - FilterKeyEvent(event); -} - -void CompoundEventFilter::OnMouseEvent(ui::MouseEvent* event) { - aura::Window* window = static_cast<aura::Window*>(event->target()); - aura::WindowTracker window_tracker; - window_tracker.Add(window); - - // We must always update the cursor, otherwise the cursor can get stuck if an - // event filter registered with us consumes the event. - // It should also update the cursor for clicking and wheels for ChromeOS boot. - // When ChromeOS is booted, it hides the mouse cursor but immediate mouse - // operation will show the cursor. - // We also update the cursor for mouse enter in case a mouse cursor is sent to - // outside of the root window and moved back for some reasons (e.g. running on - // on Desktop for testing, or a bug in pointer barrier). - if (event->type() == ui::ET_MOUSE_ENTERED || - event->type() == ui::ET_MOUSE_MOVED || - event->type() == ui::ET_MOUSE_PRESSED || - event->type() == ui::ET_MOUSEWHEEL) { - SetMouseEventsEnableStateOnEvent(window, event, true); - SetCursorVisibilityOnEvent(window, event, true); - UpdateCursor(window, event); - } - - FilterMouseEvent(event); -} - -void CompoundEventFilter::OnScrollEvent(ui::ScrollEvent* event) { -} - -void CompoundEventFilter::OnTouchEvent(ui::TouchEvent* event) { - FilterTouchEvent(event); - if (ShouldHideCursorOnTouch() && !event->handled() && - event->type() == ui::ET_TOUCH_PRESSED && - !aura::Env::GetInstance()->IsMouseButtonDown()) { - SetMouseEventsEnableStateOnEvent( - static_cast<aura::Window*>(event->target()), event, false); - } -} - -void CompoundEventFilter::OnGestureEvent(ui::GestureEvent* event) { - if (handlers_.might_have_observers()) { - ObserverListBase<ui::EventHandler>::Iterator it(handlers_); - ui::EventHandler* handler; - while (!event->stopped_propagation() && (handler = it.GetNext()) != NULL) - handler->OnGestureEvent(event); - } -} - -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/compound_event_filter.h b/chromium/ui/views/corewm/compound_event_filter.h deleted file mode 100644 index 7f724c35e06..00000000000 --- a/chromium/ui/views/corewm/compound_event_filter.h +++ /dev/null @@ -1,93 +0,0 @@ -// 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_COREWM_COMPOUND_EVENT_FILTER_H_ -#define UI_VIEWS_COREWM_COMPOUND_EVENT_FILTER_H_ - -#include "base/compiler_specific.h" -#include "base/observer_list.h" -#include "ui/events/event.h" -#include "ui/events/event_handler.h" -#include "ui/gfx/native_widget_types.h" -#include "ui/views/views_export.h" - -namespace aura { -class CursorManager; -class RootWindow; -} - -namespace ui { -class GestureEvent; -class KeyEvent; -class LocatedEvent; -class MouseEvent; -class TouchEvent; -} - -namespace views { -namespace corewm { - -// CompoundEventFilter gets all events first and can provide actions to those -// events. It implements global features such as click to activate a window and -// cursor change when moving mouse. -// Additional event filters can be added to CompoundEventFilter. Events will -// pass through those additional filters in their addition order and could be -// consumed by any of those filters. If an event is consumed by a filter, the -// rest of the filter(s) and CompoundEventFilter will not see the consumed -// event. -class VIEWS_EXPORT CompoundEventFilter : public ui::EventHandler { - public: - CompoundEventFilter(); - virtual ~CompoundEventFilter(); - - // Returns the cursor for the specified component. - static gfx::NativeCursor CursorForWindowComponent(int window_component); - - // Adds/removes additional event filters. This does not take ownership of - // the EventHandler. - // NOTE: These handlers are deprecated. Use env::AddPreTargetEventHandler etc. - // instead. - void AddHandler(ui::EventHandler* filter); - void RemoveHandler(ui::EventHandler* filter); - - private: - // Updates the cursor if the target provides a custom one, and provides - // default resize cursors for window edges. - void UpdateCursor(aura::Window* target, ui::MouseEvent* event); - - // Dispatches event to additional filters. - void FilterKeyEvent(ui::KeyEvent* event); - void FilterMouseEvent(ui::MouseEvent* event); - void FilterTouchEvent(ui::TouchEvent* event); - - // Sets the visibility of the cursor if the event is not synthesized. - void SetCursorVisibilityOnEvent(aura::Window* target, - ui::Event* event, - bool show); - - // Enables or disables mouse events if the event is not synthesized. - void SetMouseEventsEnableStateOnEvent(aura::Window* target, - ui::Event* event, - bool enable); - - // 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; - - // Additional pre-target event handlers. - ObserverList<ui::EventHandler, true> handlers_; - - // True if the cursur was hidden by the filter. - bool cursor_hidden_by_filter_; - - DISALLOW_COPY_AND_ASSIGN(CompoundEventFilter); -}; - -} // namespace corewm -} // namespace views - -#endif // UI_VIEWS_COREWM_COMPOUND_EVENT_FILTER_H_ diff --git a/chromium/ui/views/corewm/compound_event_filter_unittest.cc b/chromium/ui/views/corewm/compound_event_filter_unittest.cc deleted file mode 100644 index 6b852e4cd55..00000000000 --- a/chromium/ui/views/corewm/compound_event_filter_unittest.cc +++ /dev/null @@ -1,205 +0,0 @@ -// 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/corewm/compound_event_filter.h" - -#include "ui/aura/client/activation_client.h" -#include "ui/aura/client/cursor_client.h" -#include "ui/aura/env.h" -#include "ui/aura/root_window.h" -#include "ui/aura/test/aura_test_base.h" -#include "ui/aura/test/event_generator.h" -#include "ui/aura/test/test_cursor_client.h" -#include "ui/aura/test/test_windows.h" -#include "ui/aura/window.h" -#include "ui/events/event.h" -#include "ui/events/event_utils.h" - -namespace { - -#if defined(OS_CHROMEOS) -base::TimeDelta GetTime() { - return ui::EventTimeForNow(); -} -#endif // defined(OS_CHROMEOS) - -} - -namespace views { -namespace corewm { - -namespace { - -// An event filter that consumes all gesture events. -class ConsumeGestureEventFilter : public ui::EventHandler { - public: - ConsumeGestureEventFilter() {} - virtual ~ConsumeGestureEventFilter() {} - - private: - // Overridden from ui::EventHandler: - virtual void OnGestureEvent(ui::GestureEvent* e) OVERRIDE { - e->StopPropagation(); - } - - DISALLOW_COPY_AND_ASSIGN(ConsumeGestureEventFilter); -}; - -} // namespace - -typedef aura::test::AuraTestBase CompoundEventFilterTest; - -#if defined(OS_CHROMEOS) -// A keypress only hides the cursor on ChromeOS (crbug.com/304296). -TEST_F(CompoundEventFilterTest, CursorVisibilityChange) { - scoped_ptr<CompoundEventFilter> compound_filter(new CompoundEventFilter); - aura::Env::GetInstance()->AddPreTargetHandler(compound_filter.get()); - aura::test::TestWindowDelegate delegate; - scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(&delegate, 1234, - gfx::Rect(5, 5, 100, 100), root_window())); - window->Show(); - window->SetCapture(); - - aura::test::TestCursorClient cursor_client(root_window()); - - // Send key event to hide the cursor. - ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_A, 0, true); - dispatcher()->AsRootWindowHostDelegate()->OnHostKeyEvent(&key); - EXPECT_FALSE(cursor_client.IsCursorVisible()); - - // Synthesized mouse event should not show the cursor. - ui::MouseEvent enter(ui::ET_MOUSE_ENTERED, gfx::Point(10, 10), - gfx::Point(10, 10), 0); - enter.set_flags(enter.flags() | ui::EF_IS_SYNTHESIZED); - dispatcher()->AsRootWindowHostDelegate()->OnHostMouseEvent(&enter); - EXPECT_FALSE(cursor_client.IsCursorVisible()); - - ui::MouseEvent move(ui::ET_MOUSE_MOVED, gfx::Point(10, 10), - gfx::Point(10, 10), 0); - move.set_flags(enter.flags() | ui::EF_IS_SYNTHESIZED); - dispatcher()->AsRootWindowHostDelegate()->OnHostMouseEvent(&move); - EXPECT_FALSE(cursor_client.IsCursorVisible()); - - ui::MouseEvent real_move(ui::ET_MOUSE_MOVED, gfx::Point(10, 10), - gfx::Point(10, 10), 0); - dispatcher()->AsRootWindowHostDelegate()->OnHostMouseEvent(&real_move); - EXPECT_TRUE(cursor_client.IsCursorVisible()); - - // Send key event to hide the cursor again. - dispatcher()->AsRootWindowHostDelegate()->OnHostKeyEvent(&key); - EXPECT_FALSE(cursor_client.IsCursorVisible()); - - // Mouse synthesized exit event should not show the cursor. - ui::MouseEvent exit(ui::ET_MOUSE_EXITED, gfx::Point(10, 10), - gfx::Point(10, 10), 0); - exit.set_flags(enter.flags() | ui::EF_IS_SYNTHESIZED); - dispatcher()->AsRootWindowHostDelegate()->OnHostMouseEvent(&exit); - EXPECT_FALSE(cursor_client.IsCursorVisible()); -} - -TEST_F(CompoundEventFilterTest, TouchHidesCursor) { - scoped_ptr<CompoundEventFilter> compound_filter(new CompoundEventFilter); - aura::Env::GetInstance()->AddPreTargetHandler(compound_filter.get()); - aura::test::TestWindowDelegate delegate; - scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(&delegate, 1234, - gfx::Rect(5, 5, 100, 100), root_window())); - window->Show(); - window->SetCapture(); - - aura::test::TestCursorClient cursor_client(root_window()); - - ui::MouseEvent mouse0(ui::ET_MOUSE_MOVED, gfx::Point(10, 10), - gfx::Point(10, 10), 0); - dispatcher()->AsRootWindowHostDelegate()->OnHostMouseEvent(&mouse0); - EXPECT_TRUE(cursor_client.IsMouseEventsEnabled()); - - // This press is required for the GestureRecognizer to associate a target - // with kTouchId - ui::TouchEvent press0( - ui::ET_TOUCH_PRESSED, gfx::Point(90, 90), 1, GetTime()); - dispatcher()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press0); - EXPECT_FALSE(cursor_client.IsMouseEventsEnabled()); - - ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(10, 10), 1, GetTime()); - dispatcher()->AsRootWindowHostDelegate()->OnHostTouchEvent(&move); - EXPECT_FALSE(cursor_client.IsMouseEventsEnabled()); - - ui::TouchEvent release( - ui::ET_TOUCH_RELEASED, gfx::Point(10, 10), 1, GetTime()); - dispatcher()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release); - EXPECT_FALSE(cursor_client.IsMouseEventsEnabled()); - - ui::MouseEvent mouse1(ui::ET_MOUSE_MOVED, gfx::Point(10, 10), - gfx::Point(10, 10), 0); - // Move the cursor again. The cursor should be visible. - dispatcher()->AsRootWindowHostDelegate()->OnHostMouseEvent(&mouse1); - EXPECT_TRUE(cursor_client.IsMouseEventsEnabled()); - - // Now activate the window and press on it again. - ui::TouchEvent press1( - ui::ET_TOUCH_PRESSED, gfx::Point(90, 90), 1, GetTime()); - aura::client::GetActivationClient( - root_window())->ActivateWindow(window.get()); - dispatcher()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press1); - EXPECT_FALSE(cursor_client.IsMouseEventsEnabled()); - aura::Env::GetInstance()->RemovePreTargetHandler(compound_filter.get()); -} -#endif // defined(OS_CHROMEOS) - -// Tests that if an event filter consumes a gesture, then it doesn't focus the -// window. -TEST_F(CompoundEventFilterTest, FilterConsumedGesture) { - scoped_ptr<CompoundEventFilter> compound_filter(new CompoundEventFilter); - scoped_ptr<ui::EventHandler> gesure_handler(new ConsumeGestureEventFilter); - compound_filter->AddHandler(gesure_handler.get()); - aura::Env::GetInstance()->AddPreTargetHandler(compound_filter.get()); - aura::test::TestWindowDelegate delegate; - DCHECK(root_window()); - scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(&delegate, 1234, - gfx::Rect(5, 5, 100, 100), root_window())); - window->Show(); - - EXPECT_TRUE(window->CanFocus()); - EXPECT_FALSE(window->HasFocus()); - - // Tap on the window should not focus it since the filter will be consuming - // the gestures. - aura::test::EventGenerator generator(root_window(), gfx::Point(50, 50)); - generator.PressTouch(); - EXPECT_FALSE(window->HasFocus()); - - compound_filter->RemoveHandler(gesure_handler.get()); - aura::Env::GetInstance()->AddPreTargetHandler(compound_filter.get()); -} - -// Verifies we don't attempt to hide the mouse when the mouse is down and a -// touch event comes in. -TEST_F(CompoundEventFilterTest, DontHideWhenMouseDown) { - aura::test::EventGenerator event_generator(root_window()); - - scoped_ptr<CompoundEventFilter> compound_filter(new CompoundEventFilter); - aura::Env::GetInstance()->AddPreTargetHandler(compound_filter.get()); - aura::test::TestWindowDelegate delegate; - scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(&delegate, 1234, - gfx::Rect(5, 5, 100, 100), root_window())); - window->Show(); - - aura::test::TestCursorClient cursor_client(root_window()); - - // Move and press the mouse over the window. - event_generator.MoveMouseTo(10, 10); - EXPECT_TRUE(cursor_client.IsMouseEventsEnabled()); - event_generator.PressLeftButton(); - EXPECT_TRUE(cursor_client.IsMouseEventsEnabled()); - EXPECT_TRUE(aura::Env::GetInstance()->IsMouseButtonDown()); - - // Do a touch event. As the mouse button is down this should not disable mouse - // events. - event_generator.PressTouch(); - EXPECT_TRUE(cursor_client.IsMouseEventsEnabled()); - aura::Env::GetInstance()->RemovePreTargetHandler(compound_filter.get()); -} - -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/corewm_switches.cc b/chromium/ui/views/corewm/corewm_switches.cc deleted file mode 100644 index 8f0dd82a6a9..00000000000 --- a/chromium/ui/views/corewm/corewm_switches.cc +++ /dev/null @@ -1,21 +0,0 @@ -// 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/corewm/corewm_switches.h" - -#include "base/command_line.h" - -namespace views { -namespace corewm { -namespace switches { - -const char kNoDropShadows[] = "aura-no-shadows"; - -// If present animations are disabled. -const char kWindowAnimationsDisabled[] = - "views-corewm-window-animations-disabled"; - -} // namespace switches -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/corewm_switches.h b/chromium/ui/views/corewm/corewm_switches.h deleted file mode 100644 index 63c5251745d..00000000000 --- a/chromium/ui/views/corewm/corewm_switches.h +++ /dev/null @@ -1,27 +0,0 @@ -// 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_COREWM_COREWM_SWITCHES_H_ -#define UI_VIEWS_COREWM_COREWM_SWITCHES_H_ - -#include "build/build_config.h" -#include "ui/views/views_export.h" - -namespace views { -namespace corewm { -namespace switches { - -// Note: If you add a switch, consider if it needs to be copied to a subsequent -// command line if the process executes a new copy of itself. (For example, -// see chromeos::LoginUtil::GetOffTheRecordCommandLine().) - -// Please keep alphabetized. -VIEWS_EXPORT extern const char kNoDropShadows[]; -VIEWS_EXPORT extern const char kWindowAnimationsDisabled[]; - -} // namespace switches -} // namespace corewm -} // namespace views - -#endif // UI_VIEWS_COREWM_COREWM_SWITCHES_H_ diff --git a/chromium/ui/views/corewm/cursor_height_provider_win.cc b/chromium/ui/views/corewm/cursor_height_provider_win.cc new file mode 100644 index 00000000000..97ed7a3465e --- /dev/null +++ b/chromium/ui/views/corewm/cursor_height_provider_win.cc @@ -0,0 +1,146 @@ +// Copyright 2014 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/corewm/cursor_height_provider_win.h" + +#include <windows.h> +#include <algorithm> +#include <map> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/win/scoped_hdc.h" + +namespace { +typedef scoped_ptr<uint32_t> PixelData; +typedef std::map<HCURSOR, int> HeightStorage; + +const uint32_t kBitsPeruint32 = sizeof(uint32_t) * 8; +// All bits are 1 for transparent portion of monochromatic mask. +const uint32_t kTransparentMask = 0xffffffff; +// This is height of default pointer arrow in Windows 7. +const int kDefaultHeight = 20; +// Masks are monochromatic. +const size_t kNumberOfColors = 2; +const size_t KHeaderAndPalette = + sizeof(BITMAPINFOHEADER) + kNumberOfColors * sizeof(RGBQUAD); + +static HeightStorage* cached_heights = NULL; + +// Extracts the pixel data of provided bitmap +PixelData GetBitmapData(HBITMAP handle, const BITMAPINFO& info, HDC hdc) { + PixelData data; + // Masks are monochromatic. + DCHECK_EQ(info.bmiHeader.biBitCount, 1); + if (info.bmiHeader.biBitCount != 1) + return data.Pass(); + + // When getting pixel data palette is appended to memory pointed by + // BITMAPINFO passed so allocate additional memory to store additional data. + scoped_ptr<BITMAPINFO> header( + reinterpret_cast<BITMAPINFO*>(new char[KHeaderAndPalette])); + memcpy(header.get(), &(info.bmiHeader), sizeof(info.bmiHeader)); + + data.reset(new uint32_t[info.bmiHeader.biSizeImage / sizeof(uint32_t)]); + + int result = GetDIBits(hdc, + handle, + 0, + info.bmiHeader.biHeight, + data.get(), + header.get(), + DIB_RGB_COLORS); + + if (result == 0) + data.reset(); + + return data.Pass(); +} + +// Checks if the specifed row is transparent in provided bitmap. +bool IsRowTransparent(const PixelData& data, + const uint32_t row_size, + const uint32_t last_byte_mask, + const uint32_t y) { + // Set the padding bits to 1 to make mask matching easier. + *(data.get() + (y + 1) * row_size - 1) |= last_byte_mask; + for (uint32_t i = y * row_size; i < (y + 1) * row_size; ++i) { + if (*(data.get() + i) != kTransparentMask) + return false; + } + return true; +} + +// Gets the vertical offset between specified cursor's hotpoint and it's bottom. +// +// Gets the cursor image data and extract cursor's visible height. +// Based on that get's what should be the vertical offset between cursor's +// hot point and the tooltip. +int CalculateCursorHeight(HCURSOR cursor_handle) { + base::win::ScopedGetDC hdc(NULL); + + ICONINFO icon = {0}; + GetIconInfo(cursor_handle, &icon); + + BITMAPINFO bitmap_info = {0}; + bitmap_info.bmiHeader.biSize = sizeof(bitmap_info.bmiHeader); + if (GetDIBits(hdc, icon.hbmMask, 0, 0, NULL, &bitmap_info, DIB_RGB_COLORS) == + 0) + return kDefaultHeight; + + // Rows are padded to full DWORDs. OR with this mask will set them to 1 + // to simplify matching with |transparent_mask|. + uint32_t last_byte_mask = ~0; + const unsigned char bits_to_shift = sizeof(last_byte_mask) * 8 - + (bitmap_info.bmiHeader.biWidth % kBitsPeruint32); + if (bits_to_shift != kBitsPeruint32) + last_byte_mask = (last_byte_mask << bits_to_shift); + else + last_byte_mask = 0; + + const uint32_t row_size = + (bitmap_info.bmiHeader.biWidth + kBitsPeruint32 - 1) / kBitsPeruint32; + PixelData data(GetBitmapData(icon.hbmMask, bitmap_info, hdc)); + if (data == NULL) + return kDefaultHeight; + + const int cursor_height = GetSystemMetrics(SM_CYCURSOR); + // Crash data seems to indicate cursor_height may be bigger than the bitmap. + int i = std::max(0, static_cast<int>(bitmap_info.bmiHeader.biHeight) - + cursor_height); + for (; i < bitmap_info.bmiHeader.biHeight; ++i) { + if (!IsRowTransparent(data, row_size, last_byte_mask, i)) { + i--; + break; + } + } + return bitmap_info.bmiHeader.biHeight - i - icon.yHotspot; +} + +} // namespace + +namespace views { +namespace corewm { + +int GetCurrentCursorVisibleHeight() { + CURSORINFO cursor = {0}; + cursor.cbSize = sizeof(cursor); + GetCursorInfo(&cursor); + + if (cached_heights == NULL) + cached_heights = new HeightStorage; + + HeightStorage::const_iterator cached_height = + cached_heights->find(cursor.hCursor); + if (cached_height != cached_heights->end()) + return cached_height->second; + + const int height = CalculateCursorHeight(cursor.hCursor); + (*cached_heights)[cursor.hCursor] = height; + + return height; +} + +} // namespace corewm +} // namespace views diff --git a/chromium/ui/views/corewm/cursor_height_provider_win.h b/chromium/ui/views/corewm/cursor_height_provider_win.h new file mode 100644 index 00000000000..c93134491f2 --- /dev/null +++ b/chromium/ui/views/corewm/cursor_height_provider_win.h @@ -0,0 +1,21 @@ +// Copyright 2014 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_COREWM_CURSOR_HEIGHT_PROVIDER_WIN_H_ +#define UI_VIEWS_COREWM_CURSOR_HEIGHT_PROVIDER_WIN_H_ + +namespace views { +namespace corewm { + +// Gets the visible height of current cursor. +// +// The height is offset between cursor's hot point and it's +// bottom edge, derived from first non-transparent row of cursor's mask. + +int GetCurrentCursorVisibleHeight(); + +} // namespace corewm +} // namespace views + +#endif // UI_VIEWS_COREWM_CURSOR_HEIGHT_PROVIDER_WIN_H_ diff --git a/chromium/ui/views/corewm/cursor_manager.cc b/chromium/ui/views/corewm/cursor_manager.cc deleted file mode 100644 index 2335fdd1cbf..00000000000 --- a/chromium/ui/views/corewm/cursor_manager.cc +++ /dev/null @@ -1,235 +0,0 @@ -// 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/corewm/cursor_manager.h" - -#include "base/logging.h" -#include "ui/aura/client/cursor_client_observer.h" -#include "ui/views/corewm/native_cursor_manager.h" -#include "ui/views/corewm/native_cursor_manager_delegate.h" - -namespace views { -namespace corewm { - -namespace internal { - -// Represents the cursor state which is composed of cursor type, visibility, and -// mouse events enable state. When mouse events are disabled, the cursor is -// always invisible. -class CursorState { - public: - CursorState() - : cursor_(ui::kCursorNone), - visible_(true), - scale_(1.f), - cursor_set_(ui::CURSOR_SET_NORMAL), - mouse_events_enabled_(true), - visible_on_mouse_events_enabled_(true) { - } - - gfx::NativeCursor cursor() const { return cursor_; } - void set_cursor(gfx::NativeCursor cursor) { cursor_ = cursor; } - - bool visible() const { return visible_; } - void SetVisible(bool visible) { - if (mouse_events_enabled_) - visible_ = visible; - // Ignores the call when mouse events disabled. - } - - float scale() const { return scale_; } - void set_scale(float scale) { - scale_ = scale; - } - - ui::CursorSetType cursor_set() const { return cursor_set_; } - void set_cursor_set(ui::CursorSetType cursor_set) { - cursor_set_ = cursor_set; - } - - bool mouse_events_enabled() const { return mouse_events_enabled_; } - void SetMouseEventsEnabled(bool enabled) { - if (mouse_events_enabled_ == enabled) - return; - mouse_events_enabled_ = enabled; - - // Restores the visibility when mouse events are enabled. - if (enabled) { - visible_ = visible_on_mouse_events_enabled_; - } else { - visible_on_mouse_events_enabled_ = visible_; - visible_ = false; - } - } - - private: - gfx::NativeCursor cursor_; - bool visible_; - float scale_; - ui::CursorSetType cursor_set_; - bool mouse_events_enabled_; - - // The visibility to set when mouse events are enabled. - bool visible_on_mouse_events_enabled_; - - DISALLOW_COPY_AND_ASSIGN(CursorState); -}; - -} // namespace internal - -CursorManager::CursorManager(scoped_ptr<NativeCursorManager> delegate) - : delegate_(delegate.Pass()), - cursor_lock_count_(0), - current_state_(new internal::CursorState), - state_on_unlock_(new internal::CursorState) { -} - -CursorManager::~CursorManager() { -} - -void CursorManager::SetCursor(gfx::NativeCursor cursor) { - state_on_unlock_->set_cursor(cursor); - if (cursor_lock_count_ == 0 && - GetCursor() != state_on_unlock_->cursor()) { - delegate_->SetCursor(state_on_unlock_->cursor(), this); - } -} - -gfx::NativeCursor CursorManager::GetCursor() const { - return current_state_->cursor(); -} - -void CursorManager::ShowCursor() { - state_on_unlock_->SetVisible(true); - if (cursor_lock_count_ == 0 && - IsCursorVisible() != state_on_unlock_->visible()) { - delegate_->SetVisibility(state_on_unlock_->visible(), this); - FOR_EACH_OBSERVER(aura::client::CursorClientObserver, observers_, - OnCursorVisibilityChanged(true)); - } -} - -void CursorManager::HideCursor() { - state_on_unlock_->SetVisible(false); - if (cursor_lock_count_ == 0 && - IsCursorVisible() != state_on_unlock_->visible()) { - delegate_->SetVisibility(state_on_unlock_->visible(), this); - FOR_EACH_OBSERVER(aura::client::CursorClientObserver, observers_, - OnCursorVisibilityChanged(false)); - } -} - -bool CursorManager::IsCursorVisible() const { - return current_state_->visible(); -} - -void CursorManager::SetScale(float scale) { - state_on_unlock_->set_scale(scale); - if (GetScale() != state_on_unlock_->scale()) - delegate_->SetScale(state_on_unlock_->scale(), this); -} - -float CursorManager::GetScale() const { - return current_state_->scale(); -} - -void CursorManager::SetCursorSet(ui::CursorSetType cursor_set) { - state_on_unlock_->set_cursor_set(cursor_set); - if (GetCursorSet() != state_on_unlock_->cursor_set()) - delegate_->SetCursorSet(state_on_unlock_->cursor_set(), this); -} - -ui::CursorSetType CursorManager::GetCursorSet() const { - return current_state_->cursor_set(); -} - -void CursorManager::EnableMouseEvents() { - state_on_unlock_->SetMouseEventsEnabled(true); - if (cursor_lock_count_ == 0 && - IsMouseEventsEnabled() != state_on_unlock_->mouse_events_enabled()) { - delegate_->SetMouseEventsEnabled(state_on_unlock_->mouse_events_enabled(), - this); - } -} - -void CursorManager::DisableMouseEvents() { - state_on_unlock_->SetMouseEventsEnabled(false); - if (cursor_lock_count_ == 0 && - IsMouseEventsEnabled() != state_on_unlock_->mouse_events_enabled()) { - delegate_->SetMouseEventsEnabled(state_on_unlock_->mouse_events_enabled(), - this); - } -} - -bool CursorManager::IsMouseEventsEnabled() const { - return current_state_->mouse_events_enabled(); -} - -void CursorManager::SetDisplay(const gfx::Display& display) { - delegate_->SetDisplay(display, this); -} - -void CursorManager::LockCursor() { - cursor_lock_count_++; -} - -void CursorManager::UnlockCursor() { - cursor_lock_count_--; - DCHECK_GE(cursor_lock_count_, 0); - if (cursor_lock_count_ > 0) - return; - - if (GetCursor() != state_on_unlock_->cursor()) { - delegate_->SetCursor(state_on_unlock_->cursor(), this); - } - if (IsMouseEventsEnabled() != state_on_unlock_->mouse_events_enabled()) { - delegate_->SetMouseEventsEnabled(state_on_unlock_->mouse_events_enabled(), - this); - } - if (IsCursorVisible() != state_on_unlock_->visible()) { - delegate_->SetVisibility(state_on_unlock_->visible(), - this); - } -} - -bool CursorManager::IsCursorLocked() const { - return cursor_lock_count_ > 0; -} - -void CursorManager::AddObserver( - aura::client::CursorClientObserver* observer) { - observers_.AddObserver(observer); -} - -void CursorManager::RemoveObserver( - aura::client::CursorClientObserver* observer) { - observers_.RemoveObserver(observer); -} - -void CursorManager::CommitCursor(gfx::NativeCursor cursor) { - current_state_->set_cursor(cursor); -} - -void CursorManager::CommitVisibility(bool visible) { - // TODO(tdanderson): Find a better place for this so we don't - // notify the observers more than is necessary. - FOR_EACH_OBSERVER(aura::client::CursorClientObserver, observers_, - OnCursorVisibilityChanged(visible)); - current_state_->SetVisible(visible); -} - -void CursorManager::CommitScale(float scale) { - current_state_->set_scale(scale); -} - -void CursorManager::CommitCursorSet(ui::CursorSetType cursor_set) { - current_state_->set_cursor_set(cursor_set); -} - -void CursorManager::CommitMouseEventsEnabled(bool enabled) { - current_state_->SetMouseEventsEnabled(enabled); -} - -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/cursor_manager.h b/chromium/ui/views/corewm/cursor_manager.h deleted file mode 100644 index 29f890ae6bd..00000000000 --- a/chromium/ui/views/corewm/cursor_manager.h +++ /dev/null @@ -1,92 +0,0 @@ -// 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_COREWM_CURSOR_MANAGER_H_ -#define UI_VIEWS_COREWM_CURSOR_MANAGER_H_ - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/memory/scoped_ptr.h" -#include "base/observer_list.h" -#include "ui/aura/client/cursor_client.h" -#include "ui/base/cursor/cursor.h" -#include "ui/gfx/native_widget_types.h" -#include "ui/gfx/point.h" -#include "ui/views/corewm/native_cursor_manager_delegate.h" -#include "ui/views/views_export.h" - -namespace gfx { -class Display; -} - -namespace views { -namespace corewm { - -namespace internal { -class CursorState; -} - -class NativeCursorManager; - -// This class receives requests to change cursor properties, as well as -// requests to queue any further changes until a later time. It sends changes -// to the NativeCursorManager, which communicates back to us when these changes -// were made through the NativeCursorManagerDelegate interface. -class VIEWS_EXPORT CursorManager : public aura::client::CursorClient, - public NativeCursorManagerDelegate { - public: - CursorManager(scoped_ptr<NativeCursorManager> delegate); - virtual ~CursorManager(); - - // Overridden from aura::client::CursorClient: - virtual void SetCursor(gfx::NativeCursor) OVERRIDE; - virtual gfx::NativeCursor GetCursor() const OVERRIDE; - virtual void ShowCursor() OVERRIDE; - virtual void HideCursor() OVERRIDE; - virtual bool IsCursorVisible() const OVERRIDE; - virtual void SetScale(float scale) OVERRIDE; - virtual float GetScale() const OVERRIDE; - virtual void SetCursorSet(ui::CursorSetType cursor_set) OVERRIDE; - virtual ui::CursorSetType GetCursorSet() const OVERRIDE; - virtual void EnableMouseEvents() OVERRIDE; - virtual void DisableMouseEvents() OVERRIDE; - virtual bool IsMouseEventsEnabled() const OVERRIDE; - virtual void SetDisplay(const gfx::Display& display) OVERRIDE; - virtual void LockCursor() OVERRIDE; - virtual void UnlockCursor() OVERRIDE; - virtual bool IsCursorLocked() const OVERRIDE; - virtual void AddObserver( - aura::client::CursorClientObserver* observer) OVERRIDE; - virtual void RemoveObserver( - aura::client::CursorClientObserver* observer) OVERRIDE; - - private: - // Overridden from NativeCursorManagerDelegate: - virtual void CommitCursor(gfx::NativeCursor cursor) OVERRIDE; - virtual void CommitVisibility(bool visible) OVERRIDE; - virtual void CommitScale(float scale) OVERRIDE; - virtual void CommitCursorSet(ui::CursorSetType cursor_set) OVERRIDE; - virtual void CommitMouseEventsEnabled(bool enabled) OVERRIDE; - - scoped_ptr<NativeCursorManager> delegate_; - - // Number of times LockCursor() has been invoked without a corresponding - // UnlockCursor(). - int cursor_lock_count_; - - // The current state of the cursor. - scoped_ptr<internal::CursorState> current_state_; - - // The cursor state to restore when the cursor is unlocked. - scoped_ptr<internal::CursorState> state_on_unlock_; - - ObserverList<aura::client::CursorClientObserver> observers_; - - DISALLOW_COPY_AND_ASSIGN(CursorManager); -}; - -} // namespace corewm -} // namespace views - -#endif // UI_VIEWS_COREWM_CURSOR_MANAGER_H_ diff --git a/chromium/ui/views/corewm/cursor_manager_unittest.cc b/chromium/ui/views/corewm/cursor_manager_unittest.cc deleted file mode 100644 index a9c507782a5..00000000000 --- a/chromium/ui/views/corewm/cursor_manager_unittest.cc +++ /dev/null @@ -1,353 +0,0 @@ -// 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/corewm/cursor_manager.h" - -#include "ui/aura/client/cursor_client_observer.h" -#include "ui/views/corewm/native_cursor_manager.h" -#include "ui/views/test/views_test_base.h" - -namespace { - -class TestingCursorManager : public views::corewm::NativeCursorManager { - public: - // 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 { - delegate->CommitCursor(cursor); - } - - virtual void SetVisibility( - bool visible, - views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE { - delegate->CommitVisibility(visible); - } - - virtual void SetMouseEventsEnabled( - bool enabled, - views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE { - delegate->CommitMouseEventsEnabled(enabled); - } - - virtual void SetCursorSet( - ui::CursorSetType cursor_set, - views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE { - delegate->CommitCursorSet(cursor_set); - } - - virtual void SetScale( - float scale, - views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE { - delegate->CommitScale(scale); - } -}; - -} // namespace - -class CursorManagerTest : public views::ViewsTestBase { - protected: - CursorManagerTest() - : delegate_(new TestingCursorManager), - cursor_manager_(scoped_ptr<views::corewm::NativeCursorManager>( - delegate_)) { - } - - TestingCursorManager* delegate_; - views::corewm::CursorManager cursor_manager_; -}; - -class TestingCursorClientObserver : public aura::client::CursorClientObserver { - public: - TestingCursorClientObserver() - : cursor_visibility_(false), - did_visibility_change_(false) {} - void reset() { cursor_visibility_ = did_visibility_change_ = false; } - bool is_cursor_visible() const { return cursor_visibility_; } - bool did_visibility_change() const { return did_visibility_change_; } - - // Overridden from aura::client::CursorClientObserver: - virtual void OnCursorVisibilityChanged(bool is_visible) OVERRIDE { - cursor_visibility_ = is_visible; - did_visibility_change_ = true; - } - - private: - bool cursor_visibility_; - bool did_visibility_change_; - - DISALLOW_COPY_AND_ASSIGN(TestingCursorClientObserver); -}; - -TEST_F(CursorManagerTest, ShowHideCursor) { - cursor_manager_.SetCursor(ui::kCursorCopy); - EXPECT_EQ(ui::kCursorCopy, cursor_manager_.GetCursor().native_type()); - - cursor_manager_.ShowCursor(); - EXPECT_TRUE(cursor_manager_.IsCursorVisible()); - cursor_manager_.HideCursor(); - EXPECT_FALSE(cursor_manager_.IsCursorVisible()); - // The current cursor does not change even when the cursor is not shown. - EXPECT_EQ(ui::kCursorCopy, cursor_manager_.GetCursor().native_type()); - - // Check if cursor visibility is locked. - cursor_manager_.LockCursor(); - EXPECT_FALSE(cursor_manager_.IsCursorVisible()); - cursor_manager_.ShowCursor(); - EXPECT_FALSE(cursor_manager_.IsCursorVisible()); - cursor_manager_.UnlockCursor(); - EXPECT_TRUE(cursor_manager_.IsCursorVisible()); - - cursor_manager_.LockCursor(); - EXPECT_TRUE(cursor_manager_.IsCursorVisible()); - cursor_manager_.HideCursor(); - EXPECT_TRUE(cursor_manager_.IsCursorVisible()); - cursor_manager_.UnlockCursor(); - EXPECT_FALSE(cursor_manager_.IsCursorVisible()); - - // Checks setting visiblity while cursor is locked does not affect the - // subsequent uses of UnlockCursor. - cursor_manager_.LockCursor(); - cursor_manager_.HideCursor(); - cursor_manager_.UnlockCursor(); - EXPECT_FALSE(cursor_manager_.IsCursorVisible()); - - cursor_manager_.ShowCursor(); - cursor_manager_.LockCursor(); - cursor_manager_.UnlockCursor(); - EXPECT_TRUE(cursor_manager_.IsCursorVisible()); - - cursor_manager_.LockCursor(); - cursor_manager_.ShowCursor(); - cursor_manager_.UnlockCursor(); - EXPECT_TRUE(cursor_manager_.IsCursorVisible()); - - cursor_manager_.HideCursor(); - cursor_manager_.LockCursor(); - cursor_manager_.UnlockCursor(); - EXPECT_FALSE(cursor_manager_.IsCursorVisible()); -} - -// Verifies that LockCursor/UnlockCursor work correctly with -// EnableMouseEvents and DisableMouseEvents -TEST_F(CursorManagerTest, EnableDisableMouseEvents) { - cursor_manager_.SetCursor(ui::kCursorCopy); - EXPECT_EQ(ui::kCursorCopy, cursor_manager_.GetCursor().native_type()); - - cursor_manager_.EnableMouseEvents(); - EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled()); - cursor_manager_.DisableMouseEvents(); - EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled()); - // The current cursor does not change even when the cursor is not shown. - EXPECT_EQ(ui::kCursorCopy, cursor_manager_.GetCursor().native_type()); - - // Check if cursor enable state is locked. - cursor_manager_.LockCursor(); - EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled()); - cursor_manager_.EnableMouseEvents(); - EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled()); - cursor_manager_.UnlockCursor(); - EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled()); - - cursor_manager_.LockCursor(); - EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled()); - cursor_manager_.DisableMouseEvents(); - EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled()); - cursor_manager_.UnlockCursor(); - EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled()); - - // Checks enabling cursor while cursor is locked does not affect the - // subsequent uses of UnlockCursor. - cursor_manager_.LockCursor(); - cursor_manager_.DisableMouseEvents(); - cursor_manager_.UnlockCursor(); - EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled()); - - cursor_manager_.EnableMouseEvents(); - cursor_manager_.LockCursor(); - cursor_manager_.UnlockCursor(); - EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled()); - - cursor_manager_.LockCursor(); - cursor_manager_.EnableMouseEvents(); - cursor_manager_.UnlockCursor(); - EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled()); - - cursor_manager_.DisableMouseEvents(); - cursor_manager_.LockCursor(); - cursor_manager_.UnlockCursor(); - EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled()); -} - -TEST_F(CursorManagerTest, SetCursorSet) { - EXPECT_EQ(ui::CURSOR_SET_NORMAL, cursor_manager_.GetCursorSet()); - - cursor_manager_.SetCursorSet(ui::CURSOR_SET_NORMAL); - EXPECT_EQ(ui::CURSOR_SET_NORMAL, cursor_manager_.GetCursorSet()); - - cursor_manager_.SetCursorSet(ui::CURSOR_SET_LARGE); - EXPECT_EQ(ui::CURSOR_SET_LARGE, cursor_manager_.GetCursorSet()); - - cursor_manager_.SetCursorSet(ui::CURSOR_SET_NORMAL); - EXPECT_EQ(ui::CURSOR_SET_NORMAL, cursor_manager_.GetCursorSet()); -} - -TEST_F(CursorManagerTest, SetScale) { - EXPECT_EQ(1.f, cursor_manager_.GetScale()); - cursor_manager_.SetScale(2.f); - EXPECT_EQ(2.f, cursor_manager_.GetScale()); - - // Cusror scale does change even while cursor is locked. - cursor_manager_.LockCursor(); - EXPECT_EQ(2.f, cursor_manager_.GetScale()); - cursor_manager_.SetScale(2.5f); - EXPECT_EQ(2.5f, cursor_manager_.GetScale()); - cursor_manager_.UnlockCursor(); - - EXPECT_EQ(2.5f, cursor_manager_.GetScale()); - cursor_manager_.SetScale(1.f); - EXPECT_EQ(1.f, cursor_manager_.GetScale()); -} - -TEST_F(CursorManagerTest, IsMouseEventsEnabled) { - cursor_manager_.EnableMouseEvents(); - EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled()); - cursor_manager_.DisableMouseEvents(); - EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled()); -} - -// Verifies that the mouse events enable state changes correctly when -// ShowCursor/HideCursor and EnableMouseEvents/DisableMouseEvents are used -// together. -TEST_F(CursorManagerTest, ShowAndEnable) { - // Changing the visibility of the cursor does not affect the enable state. - cursor_manager_.EnableMouseEvents(); - cursor_manager_.ShowCursor(); - EXPECT_TRUE(cursor_manager_.IsCursorVisible()); - EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled()); - cursor_manager_.HideCursor(); - EXPECT_FALSE(cursor_manager_.IsCursorVisible()); - EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled()); - cursor_manager_.ShowCursor(); - EXPECT_TRUE(cursor_manager_.IsCursorVisible()); - EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled()); - - // When mouse events are disabled, it also gets invisible. - EXPECT_TRUE(cursor_manager_.IsCursorVisible()); - cursor_manager_.DisableMouseEvents(); - EXPECT_FALSE(cursor_manager_.IsCursorVisible()); - EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled()); - - // When mouse events are enabled, it restores the visibility state. - cursor_manager_.EnableMouseEvents(); - EXPECT_TRUE(cursor_manager_.IsCursorVisible()); - EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled()); - - cursor_manager_.ShowCursor(); - cursor_manager_.DisableMouseEvents(); - EXPECT_FALSE(cursor_manager_.IsCursorVisible()); - EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled()); - cursor_manager_.EnableMouseEvents(); - EXPECT_TRUE(cursor_manager_.IsCursorVisible()); - EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled()); - - cursor_manager_.HideCursor(); - cursor_manager_.DisableMouseEvents(); - EXPECT_FALSE(cursor_manager_.IsCursorVisible()); - EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled()); - cursor_manager_.EnableMouseEvents(); - EXPECT_FALSE(cursor_manager_.IsCursorVisible()); - EXPECT_TRUE(cursor_manager_.IsMouseEventsEnabled()); - - // When mouse events are disabled, ShowCursor is ignored. - cursor_manager_.DisableMouseEvents(); - EXPECT_FALSE(cursor_manager_.IsCursorVisible()); - EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled()); - cursor_manager_.ShowCursor(); - EXPECT_FALSE(cursor_manager_.IsCursorVisible()); - EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled()); - cursor_manager_.DisableMouseEvents(); - EXPECT_FALSE(cursor_manager_.IsCursorVisible()); - EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled()); -} - -// Verifies that calling DisableMouseEvents multiple times in a row makes no -// difference compared with calling it once. -// This is a regression test for http://crbug.com/169404. -TEST_F(CursorManagerTest, MultipleDisableMouseEvents) { - cursor_manager_.DisableMouseEvents(); - cursor_manager_.DisableMouseEvents(); - cursor_manager_.EnableMouseEvents(); - cursor_manager_.LockCursor(); - cursor_manager_.UnlockCursor(); - EXPECT_TRUE(cursor_manager_.IsCursorVisible()); -} - -// Verifies that calling EnableMouseEvents multiple times in a row makes no -// difference compared with calling it once. -TEST_F(CursorManagerTest, MultipleEnableMouseEvents) { - cursor_manager_.DisableMouseEvents(); - cursor_manager_.EnableMouseEvents(); - cursor_manager_.EnableMouseEvents(); - cursor_manager_.LockCursor(); - cursor_manager_.UnlockCursor(); - EXPECT_TRUE(cursor_manager_.IsCursorVisible()); -} - -TEST_F(CursorManagerTest, TestCursorClientObserver) { - // Add two observers. Both should have OnCursorVisibilityChanged() - // invoked when the visibility of the cursor changes. - TestingCursorClientObserver observer_a; - TestingCursorClientObserver observer_b; - cursor_manager_.AddObserver(&observer_a); - cursor_manager_.AddObserver(&observer_b); - - // Initial state before any events have been sent. - observer_a.reset(); - observer_b.reset(); - EXPECT_FALSE(observer_a.did_visibility_change()); - EXPECT_FALSE(observer_b.did_visibility_change()); - EXPECT_FALSE(observer_a.is_cursor_visible()); - EXPECT_FALSE(observer_b.is_cursor_visible()); - - // Hide the cursor using HideCursor(). - cursor_manager_.HideCursor(); - EXPECT_TRUE(observer_a.did_visibility_change()); - EXPECT_TRUE(observer_b.did_visibility_change()); - EXPECT_FALSE(observer_a.is_cursor_visible()); - EXPECT_FALSE(observer_b.is_cursor_visible()); - - // Show the cursor using ShowCursor(). - observer_a.reset(); - observer_b.reset(); - cursor_manager_.ShowCursor(); - EXPECT_TRUE(observer_a.did_visibility_change()); - EXPECT_TRUE(observer_b.did_visibility_change()); - EXPECT_TRUE(observer_a.is_cursor_visible()); - EXPECT_TRUE(observer_b.is_cursor_visible()); - - // Remove observer_b. Its OnCursorVisibilityChanged() should - // not be invoked past this point. - cursor_manager_.RemoveObserver(&observer_b); - - // Hide the cursor using HideCursor(). - observer_a.reset(); - observer_b.reset(); - cursor_manager_.HideCursor(); - EXPECT_TRUE(observer_a.did_visibility_change()); - EXPECT_FALSE(observer_b.did_visibility_change()); - EXPECT_FALSE(observer_a.is_cursor_visible()); - - // Show the cursor using ShowCursor(). - observer_a.reset(); - observer_b.reset(); - cursor_manager_.ShowCursor(); - EXPECT_TRUE(observer_a.did_visibility_change()); - EXPECT_FALSE(observer_b.did_visibility_change()); - EXPECT_TRUE(observer_a.is_cursor_visible()); -} diff --git a/chromium/ui/views/corewm/desktop_capture_controller_unittest.cc b/chromium/ui/views/corewm/desktop_capture_controller_unittest.cc index c655ec8081c..8807aafdd6e 100644 --- a/chromium/ui/views/corewm/desktop_capture_controller_unittest.cc +++ b/chromium/ui/views/corewm/desktop_capture_controller_unittest.cc @@ -2,14 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/views/corewm/capture_controller.h" +#include "ui/wm/core/capture_controller.h" #include "base/logging.h" +#include "base/path_service.h" #include "ui/aura/env.h" -#include "ui/aura/root_window.h" #include "ui/aura/test/event_generator.h" #include "ui/aura/test/test_window_delegate.h" +#include "ui/aura/window_event_dispatcher.h" +#include "ui/aura/window_tree_host.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/base/ui_base_paths.h" #include "ui/events/event.h" +#include "ui/gl/gl_surface.h" #include "ui/views/test/views_test_base.h" #include "ui/views/view.h" #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" @@ -22,7 +27,22 @@ namespace views { -typedef ViewsTestBase DesktopCaptureControllerTest; +class DesktopCaptureControllerTest : public ViewsTestBase { + public: + DesktopCaptureControllerTest() {} + virtual ~DesktopCaptureControllerTest() {} + + virtual void SetUp() OVERRIDE { + gfx::GLSurface::InitializeOneOffForTests(); + base::FilePath pak_dir; + PathService::Get(base::DIR_MODULE, &pak_dir); + base::FilePath pak_file; + pak_file = pak_dir.Append(FILE_PATH_LITERAL("ui_test.pak")); + ui::ResourceBundle::InitSharedInstanceWithPakPath(pak_file); + + ViewsTestBase::SetUp(); + } +}; // This class provides functionality to verify whether the View instance // received the gesture event. @@ -72,13 +92,16 @@ TEST_F(DesktopCaptureControllerTest, ResetMouseHandlers) { generator1.MoveMouseToCenterOf(w1->GetNativeView()); generator1.PressLeftButton(); EXPECT_FALSE(w1->HasCapture()); - aura::RootWindow* w1_root = w1->GetNativeView()->GetDispatcher(); - EXPECT_TRUE(w1_root->mouse_pressed_handler() != NULL); - EXPECT_TRUE(w1_root->mouse_moved_handler() != NULL); + aura::WindowEventDispatcher* w1_dispatcher = + w1->GetNativeView()->GetHost()->dispatcher(); + EXPECT_TRUE(w1_dispatcher->mouse_pressed_handler() != NULL); + EXPECT_TRUE(w1_dispatcher->mouse_moved_handler() != NULL); w2->SetCapture(w2->GetRootView()); EXPECT_TRUE(w2->HasCapture()); - EXPECT_TRUE(w1_root->mouse_pressed_handler() == NULL); - EXPECT_TRUE(w1_root->mouse_moved_handler() == NULL); + EXPECT_TRUE(w1_dispatcher->mouse_pressed_handler() == NULL); + EXPECT_TRUE(w1_dispatcher->mouse_moved_handler() == NULL); + w2->ReleaseCapture(); + RunPendingMessages(); } // Tests aura::Window capture and whether gesture events are sent to the window @@ -93,8 +116,8 @@ TEST_F(DesktopCaptureControllerTest, CaptureWindowInputEventTest) { scoped_ptr<Widget> widget1(new Widget()); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); - scoped_ptr<corewm::ScopedCaptureClient> scoped_capture_client( - new corewm::ScopedCaptureClient(params.context->GetRootWindow())); + scoped_ptr<wm::ScopedCaptureClient> scoped_capture_client( + new wm::ScopedCaptureClient(params.context->GetRootWindow())); aura::client::CaptureClient* capture_client = scoped_capture_client->capture_client(); params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; @@ -103,7 +126,8 @@ TEST_F(DesktopCaptureControllerTest, CaptureWindowInputEventTest) { internal::RootView* root1 = static_cast<internal::RootView*>(widget1->GetRootView()); - desktop_position_client1.reset(new DesktopScreenPositionClient()); + desktop_position_client1.reset( + new DesktopScreenPositionClient(params.context->GetRootWindow())); aura::client::SetScreenPositionClient( widget1->GetNativeView()->GetRootWindow(), desktop_position_client1.get()); @@ -122,10 +146,12 @@ TEST_F(DesktopCaptureControllerTest, CaptureWindowInputEventTest) { internal::RootView* root2 = static_cast<internal::RootView*>(widget2->GetRootView()); - desktop_position_client2.reset(new DesktopScreenPositionClient()); + desktop_position_client2.reset( + new DesktopScreenPositionClient(params.context->GetRootWindow())); aura::client::SetScreenPositionClient( widget2->GetNativeView()->GetRootWindow(), desktop_position_client2.get()); + ui::EventDispatchDetails details; DesktopViewInputTest* v2 = new DesktopViewInputTest(); v2->SetBoundsRect(gfx::Rect(0, 0, 300, 300)); @@ -146,7 +172,10 @@ TEST_F(DesktopCaptureControllerTest, CaptureWindowInputEventTest) { base::TimeDelta(), ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS, 0.0f, 0.0f), 0); - root1->DispatchGestureEvent(&g1); + details = root1->OnEventFromSource(&g1); + EXPECT_FALSE(details.dispatcher_destroyed); + EXPECT_FALSE(details.target_destroyed); + EXPECT_TRUE(v1->received_gesture_event()); EXPECT_FALSE(v2->received_gesture_event()); v1->Reset(); @@ -158,7 +187,10 @@ TEST_F(DesktopCaptureControllerTest, CaptureWindowInputEventTest) { EXPECT_TRUE(widget2->GetNativeView()->HasCapture()); EXPECT_EQ(capture_client->GetCaptureWindow(), widget2->GetNativeView()); - root2->DispatchGestureEvent(&g1); + details = root2->OnEventFromSource(&g1); + EXPECT_FALSE(details.dispatcher_destroyed); + EXPECT_FALSE(details.target_destroyed); + EXPECT_TRUE(v2->received_gesture_event()); EXPECT_FALSE(v1->received_gesture_event()); diff --git a/chromium/ui/views/corewm/focus_controller.cc b/chromium/ui/views/corewm/focus_controller.cc deleted file mode 100644 index 17add8417fb..00000000000 --- a/chromium/ui/views/corewm/focus_controller.cc +++ /dev/null @@ -1,365 +0,0 @@ -// 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/corewm/focus_controller.h" - -#include "base/auto_reset.h" -#include "ui/aura/client/activation_change_observer.h" -#include "ui/aura/client/aura_constants.h" -#include "ui/aura/client/capture_client.h" -#include "ui/aura/client/focus_change_observer.h" -#include "ui/aura/env.h" -#include "ui/aura/window_tracker.h" -#include "ui/events/event.h" -#include "ui/views/corewm/focus_rules.h" - -namespace views { -namespace corewm { -namespace { - -// When a modal window is activated, we bring its entire transient parent chain -// to the front. This function must be called before the modal transient is -// stacked at the top to ensure correct stacking order. -void StackTransientParentsBelowModalWindow(aura::Window* window) { - if (window->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_WINDOW) - return; - - aura::Window* transient_parent = window->transient_parent(); - while (transient_parent) { - transient_parent->parent()->StackChildAtTop(transient_parent); - transient_parent = transient_parent->transient_parent(); - } -} - -// Stack's |window|'s layer above |relative_to|'s layer. -void StackWindowLayerAbove(aura::Window* window, aura::Window* relative_to) { - // Stack |window| above the last transient child of |relative_to| that shares - // the same parent. - const aura::Window::Windows& window_transients( - relative_to->transient_children()); - for (aura::Window::Windows::const_iterator i = window_transients.begin(); - i != window_transients.end(); ++i) { - aura::Window* transient = *i; - if (transient->parent() == relative_to->parent()) - relative_to = transient; - } - if (window != relative_to) { - window->layer()->parent()->StackAbove(window->layer(), - relative_to->layer()); - } -} - -} // namespace - -//////////////////////////////////////////////////////////////////////////////// -// FocusController, public: - -FocusController::FocusController(FocusRules* rules) - : active_window_(NULL), - focused_window_(NULL), - updating_focus_(false), - updating_activation_(false), - rules_(rules), - observer_manager_(this) { - DCHECK(rules); -} - -FocusController::~FocusController() { -} - -//////////////////////////////////////////////////////////////////////////////// -// FocusController, aura::client::ActivationClient implementation: - -void FocusController::AddObserver( - aura::client::ActivationChangeObserver* observer) { - activation_observers_.AddObserver(observer); -} - -void FocusController::RemoveObserver( - aura::client::ActivationChangeObserver* observer) { - activation_observers_.RemoveObserver(observer); -} - -void FocusController::ActivateWindow(aura::Window* window) { - FocusWindow(window); -} - -void FocusController::DeactivateWindow(aura::Window* window) { - if (window) - FocusWindow(rules_->GetNextActivatableWindow(window)); -} - -aura::Window* FocusController::GetActiveWindow() { - return active_window_; -} - -aura::Window* FocusController::GetActivatableWindow(aura::Window* window) { - return rules_->GetActivatableWindow(window); -} - -aura::Window* FocusController::GetToplevelWindow(aura::Window* window) { - return rules_->GetToplevelWindow(window); -} - -bool FocusController::OnWillFocusWindow(aura::Window* window, - const ui::Event* event) { - NOTREACHED(); - return false; -} - -bool FocusController::CanActivateWindow(aura::Window* window) const { - return rules_->CanActivateWindow(window); -} - -//////////////////////////////////////////////////////////////////////////////// -// FocusController, aura::client::FocusClient implementation: - -void FocusController::AddObserver( - aura::client::FocusChangeObserver* observer) { - focus_observers_.AddObserver(observer); -} - -void FocusController::RemoveObserver( - aura::client::FocusChangeObserver* observer) { - focus_observers_.RemoveObserver(observer); -} - -void FocusController::FocusWindow(aura::Window* window) { - if (window && - (window->Contains(focused_window_) || window->Contains(active_window_))) { - return; - } - - // We should not be messing with the focus if the window has capture, unless - // no has focus. - if (window && (aura::client::GetCaptureWindow(window) == window) && - focused_window_) { - return; - } - - // Focusing a window also activates its containing activatable window. Note - // that the rules could redirect activation activation and/or focus. - aura::Window* focusable = rules_->GetFocusableWindow(window); - aura::Window* activatable = - focusable ? rules_->GetActivatableWindow(focusable) : NULL; - - // We need valid focusable/activatable windows in the event we're not clearing - // focus. "Clearing focus" is inferred by whether or not |window| passed to - // this function is non-NULL. - if (window && (!focusable || !activatable)) - return; - DCHECK((focusable && activatable) || !window); - - // Activation change observers may change the focused window. If this happens - // we must not adjust the focus below since this will clobber that change. - aura::Window* last_focused_window = focused_window_; - if (!updating_activation_) - SetActiveWindow(window, activatable); - - // If the window's ActivationChangeObserver shifted focus to a valid window, - // we don't want to focus the window we thought would be focused by default. - bool activation_changed_focus = last_focused_window != focused_window_; - if (!updating_focus_ && (!activation_changed_focus || !focused_window_)) { - if (active_window_ && focusable) - DCHECK(active_window_->Contains(focusable)); - SetFocusedWindow(focusable); - } -} - -void FocusController::ResetFocusWithinActiveWindow(aura::Window* window) { - DCHECK(window); - if (!active_window_) - return; - if (!active_window_->Contains(window)) - return; - SetFocusedWindow(window); -} - -aura::Window* FocusController::GetFocusedWindow() { - return focused_window_; -} - -//////////////////////////////////////////////////////////////////////////////// -// FocusController, ui::EventHandler implementation: -void FocusController::OnKeyEvent(ui::KeyEvent* event) { -} - -void FocusController::OnMouseEvent(ui::MouseEvent* event) { - if (event->type() == ui::ET_MOUSE_PRESSED) - WindowFocusedFromInputEvent(static_cast<aura::Window*>(event->target())); -} - -void FocusController::OnScrollEvent(ui::ScrollEvent* event) { -} - -void FocusController::OnTouchEvent(ui::TouchEvent* event) { -} - -void FocusController::OnGestureEvent(ui::GestureEvent* event) { - if (event->type() == ui::ET_GESTURE_BEGIN && - event->details().touch_points() == 1) { - WindowFocusedFromInputEvent(static_cast<aura::Window*>(event->target())); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// FocusController, aura::WindowObserver implementation: - -void FocusController::OnWindowVisibilityChanged(aura::Window* window, - bool visible) { - if (!visible) { - WindowLostFocusFromDispositionChange(window, window->parent()); - // Despite the focus change, we need to keep the window being hidden - // stacked above the new window so it stays open on top as it animates away. - aura::Window* next_window = GetActiveWindow(); - if (next_window && next_window->parent() == window->parent()) - StackWindowLayerAbove(window, next_window); - } -} - -void FocusController::OnWindowDestroying(aura::Window* window) { - WindowLostFocusFromDispositionChange(window, window->parent()); -} - -void FocusController::OnWindowHierarchyChanging( - const HierarchyChangeParams& params) { - if (params.receiver == active_window_ && - params.target->Contains(params.receiver) && (!params.new_parent || - aura::client::GetFocusClient(params.new_parent) != - aura::client::GetFocusClient(params.receiver))) { - WindowLostFocusFromDispositionChange(params.receiver, params.old_parent); - } -} - -void FocusController::OnWindowHierarchyChanged( - const HierarchyChangeParams& params) { - if (params.receiver == focused_window_ && - params.target->Contains(params.receiver) && (!params.new_parent || - aura::client::GetFocusClient(params.new_parent) != - aura::client::GetFocusClient(params.receiver))) { - WindowLostFocusFromDispositionChange(params.receiver, params.old_parent); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// FocusController, private: - -void FocusController::SetFocusedWindow(aura::Window* window) { - if (updating_focus_ || window == focused_window_) - return; - DCHECK(rules_->CanFocusWindow(window)); - if (window) - DCHECK_EQ(window, rules_->GetFocusableWindow(window)); - - base::AutoReset<bool> updating_focus(&updating_focus_, true); - aura::Window* lost_focus = focused_window_; - if (focused_window_ && observer_manager_.IsObserving(focused_window_) && - focused_window_ != active_window_) { - observer_manager_.Remove(focused_window_); - } - focused_window_ = window; - if (focused_window_ && !observer_manager_.IsObserving(focused_window_)) - observer_manager_.Add(focused_window_); - - FOR_EACH_OBSERVER(aura::client::FocusChangeObserver, - focus_observers_, - OnWindowFocused(focused_window_, lost_focus)); - aura::client::FocusChangeObserver* observer = - aura::client::GetFocusChangeObserver(lost_focus); - if (observer) - observer->OnWindowFocused(focused_window_, lost_focus); - observer = aura::client::GetFocusChangeObserver(focused_window_); - if (observer) - observer->OnWindowFocused(focused_window_, lost_focus); -} - -void FocusController::SetActiveWindow(aura::Window* requested_window, - aura::Window* window) { - if (updating_activation_) - return; - - if (window == active_window_) { - if (requested_window) { - FOR_EACH_OBSERVER(aura::client::ActivationChangeObserver, - activation_observers_, - OnAttemptToReactivateWindow(requested_window, - active_window_)); - } - return; - } - - DCHECK(rules_->CanActivateWindow(window)); - if (window) - DCHECK_EQ(window, rules_->GetActivatableWindow(window)); - - base::AutoReset<bool> updating_activation(&updating_activation_, true); - aura::Window* lost_activation = active_window_; - // Allow for the window losing activation to be deleted during dispatch. If - // it is deleted pass NULL to observers instead of a deleted window. - aura::WindowTracker window_tracker; - if (lost_activation) - window_tracker.Add(lost_activation); - if (active_window_ && observer_manager_.IsObserving(active_window_) && - focused_window_ != active_window_) { - observer_manager_.Remove(active_window_); - } - active_window_ = window; - if (active_window_ && !observer_manager_.IsObserving(active_window_)) - observer_manager_.Add(active_window_); - if (active_window_) { - StackTransientParentsBelowModalWindow(active_window_); - active_window_->parent()->StackChildAtTop(active_window_); - } - - aura::client::ActivationChangeObserver* observer = NULL; - if (window_tracker.Contains(lost_activation)) { - observer = aura::client::GetActivationChangeObserver(lost_activation); - if (observer) - observer->OnWindowActivated(active_window_, lost_activation); - } - observer = aura::client::GetActivationChangeObserver(active_window_); - if (observer) { - observer->OnWindowActivated( - active_window_, - window_tracker.Contains(lost_activation) ? lost_activation : NULL); - } - FOR_EACH_OBSERVER(aura::client::ActivationChangeObserver, - activation_observers_, - OnWindowActivated(active_window_, - window_tracker.Contains(lost_activation) ? - lost_activation : NULL)); -} - -void FocusController::WindowLostFocusFromDispositionChange( - aura::Window* window, - aura::Window* next) { - // A window's modality state will interfere with focus restoration during its - // destruction. - window->ClearProperty(aura::client::kModalKey); - // TODO(beng): See if this function can be replaced by a call to - // FocusWindow(). - // Activation adjustments are handled first in the event of a disposition - // changed. If an activation change is necessary, focus is reset as part of - // that process so there's no point in updating focus independently. - if (window == active_window_) { - aura::Window* next_activatable = rules_->GetNextActivatableWindow(window); - SetActiveWindow(NULL, next_activatable); - if (!(active_window_ && active_window_->Contains(focused_window_))) - SetFocusedWindow(next_activatable); - } else if (window->Contains(focused_window_)) { - // Active window isn't changing, but focused window might be. - SetFocusedWindow(rules_->GetFocusableWindow(next)); - } -} - -void FocusController::WindowFocusedFromInputEvent(aura::Window* window) { - // Only focus |window| if it or any of its parents can be focused. Otherwise - // FocusWindow() will focus the topmost window, which may not be the - // currently focused one. - if (rules_->CanFocusWindow(GetToplevelWindow(window))) - FocusWindow(window); -} - -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/focus_controller.h b/chromium/ui/views/corewm/focus_controller.h deleted file mode 100644 index 1dc69b78219..00000000000 --- a/chromium/ui/views/corewm/focus_controller.h +++ /dev/null @@ -1,130 +0,0 @@ -// 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_COREWM_FOCUS_CONTROLLER_H_ -#define UI_VIEWS_COREWM_FOCUS_CONTROLLER_H_ - -#include "base/compiler_specific.h" -#include "base/memory/scoped_ptr.h" -#include "base/observer_list.h" -#include "base/scoped_observer.h" -#include "ui/aura/client/activation_client.h" -#include "ui/aura/client/focus_client.h" -#include "ui/aura/window_observer.h" -#include "ui/events/event_handler.h" -#include "ui/views/views_export.h" - -namespace views { -namespace corewm { - -class FocusRules; - -// FocusController handles focus and activation changes for an environment -// encompassing one or more RootWindows. Within an environment there can be -// only one focused and one active window at a time. When focus or activation -// changes notifications are sent using the -// aura::client::Focus/ActivationChangeObserver interfaces. -// Changes to focus and activation can be from three sources: -// . the Aura Client API (implemented here in aura::client::ActivationClient). -// (The FocusController must be set as the ActivationClient implementation -// for all RootWindows). -// . input events (implemented here in ui::EventHandler). -// (The FocusController must be registered as a pre-target handler for -// the applicable environment owner, either a RootWindow or another type). -// . Window disposition changes (implemented here in aura::WindowObserver). -// (The FocusController registers itself as an observer of the active and -// focused windows). -class VIEWS_EXPORT FocusController : public aura::client::ActivationClient, - public aura::client::FocusClient, - public ui::EventHandler, - public aura::WindowObserver { - public: - // |rules| cannot be NULL. - explicit FocusController(FocusRules* rules); - virtual ~FocusController(); - - private: - // Overridden from aura::client::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::client::FocusClient: - virtual void AddObserver( - aura::client::FocusChangeObserver* observer) OVERRIDE; - virtual void RemoveObserver( - aura::client::FocusChangeObserver* observer) OVERRIDE; - virtual void FocusWindow(aura::Window* window) OVERRIDE; - virtual void ResetFocusWithinActiveWindow(aura::Window* window) OVERRIDE; - virtual aura::Window* GetFocusedWindow() 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::WindowObserver: - virtual void OnWindowVisibilityChanged(aura::Window* window, - bool visible) OVERRIDE; - virtual void OnWindowDestroying(aura::Window* window) OVERRIDE; - virtual void OnWindowHierarchyChanging( - const HierarchyChangeParams& params) OVERRIDE; - virtual void OnWindowHierarchyChanged( - const HierarchyChangeParams& params) OVERRIDE; - - // Internal implementation that sets the focused window, fires events etc. - // This function must be called with a valid focusable window. - void SetFocusedWindow(aura::Window* window); - - // Internal implementation that sets the active window, fires events etc. - // This function must be called with a valid |activatable_window|. - // |requested window| refers to the window that was passed in to an external - // request (e.g. FocusWindow or ActivateWindow). It may be NULL, e.g. if - // SetActiveWindow was not called by an external request. |activatable_window| - // refers to the actual window to be activated, which may be different. - void SetActiveWindow(aura::Window* requested_window, - aura::Window* activatable_window); - - // Called when a window's disposition changed such that it and its hierarchy - // are no longer focusable/activatable. |next| is a valid window that is used - // as a starting point for finding a window to focus next based on rules. - void WindowLostFocusFromDispositionChange(aura::Window* window, - aura::Window* next); - - // Called when an attempt is made to focus or activate a window via an input - // event targeted at that window. Rules determine the best focusable window - // for the input window. - void WindowFocusedFromInputEvent(aura::Window* window); - - aura::Window* active_window_; - aura::Window* focused_window_; - - bool updating_focus_; - bool updating_activation_; - - scoped_ptr<FocusRules> rules_; - - ObserverList<aura::client::ActivationChangeObserver> activation_observers_; - ObserverList<aura::client::FocusChangeObserver> focus_observers_; - - ScopedObserver<aura::Window, aura::WindowObserver> observer_manager_; - - DISALLOW_COPY_AND_ASSIGN(FocusController); -}; - -} // namespace corewm -} // namespace views - -#endif // UI_VIEWS_COREWM_FOCUS_CONTROLLER_H_ diff --git a/chromium/ui/views/corewm/focus_controller_unittest.cc b/chromium/ui/views/corewm/focus_controller_unittest.cc deleted file mode 100644 index cfccd056c97..00000000000 --- a/chromium/ui/views/corewm/focus_controller_unittest.cc +++ /dev/null @@ -1,1110 +0,0 @@ -// 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/corewm/focus_controller.h" - -#include <map> - -#include "ui/aura/client/activation_change_observer.h" -#include "ui/aura/client/activation_client.h" -#include "ui/aura/client/aura_constants.h" -#include "ui/aura/client/default_capture_client.h" -#include "ui/aura/client/focus_change_observer.h" -#include "ui/aura/root_window.h" -#include "ui/aura/test/aura_test_base.h" -#include "ui/aura/test/event_generator.h" -#include "ui/aura/test/test_window_delegate.h" -#include "ui/aura/test/test_windows.h" -#include "ui/aura/window.h" -#include "ui/aura/window_tracker.h" -#include "ui/events/event_handler.h" -#include "ui/views/corewm/base_focus_rules.h" - -namespace views { -namespace corewm { - -class FocusNotificationObserver : public aura::client::ActivationChangeObserver, - public aura::client::FocusChangeObserver { - public: - FocusNotificationObserver() - : activation_changed_count_(0), - focus_changed_count_(0), - reactivation_count_(0), - reactivation_requested_window_(NULL), - reactivation_actual_window_(NULL) {} - virtual ~FocusNotificationObserver() {} - - void ExpectCounts(int activation_changed_count, int focus_changed_count) { - EXPECT_EQ(activation_changed_count, activation_changed_count_); - EXPECT_EQ(focus_changed_count, focus_changed_count_); - } - int reactivation_count() const { - return reactivation_count_; - } - aura::Window* reactivation_requested_window() const { - return reactivation_requested_window_; - } - aura::Window* reactivation_actual_window() const { - return reactivation_actual_window_; - } - - private: - // Overridden from aura::client::ActivationChangeObserver: - virtual void OnWindowActivated(aura::Window* gained_active, - aura::Window* lost_active) OVERRIDE { - ++activation_changed_count_; - } - virtual void OnAttemptToReactivateWindow( - aura::Window* request_active, - aura::Window* actual_active) OVERRIDE { - ++reactivation_count_; - reactivation_requested_window_ = request_active; - reactivation_actual_window_ = actual_active; - } - - // Overridden from aura::client::FocusChangeObserver: - virtual void OnWindowFocused(aura::Window* gained_focus, - aura::Window* lost_focus) OVERRIDE { - ++focus_changed_count_; - } - - int activation_changed_count_; - int focus_changed_count_; - int reactivation_count_; - aura::Window* reactivation_requested_window_; - aura::Window* reactivation_actual_window_; - - DISALLOW_COPY_AND_ASSIGN(FocusNotificationObserver); -}; - -// ActivationChangeObserver that keeps a vector of all the windows that lost -// active. -class RecordingActivationChangeObserver - : public aura::client::ActivationChangeObserver { - public: - explicit RecordingActivationChangeObserver(aura::Window* root) - : root_(root) { - aura::client::GetActivationClient(root_)->AddObserver(this); - } - virtual ~RecordingActivationChangeObserver() { - aura::client::GetActivationClient(root_)->RemoveObserver(this); - } - - // Each time we get OnWindowActivated() the |lost_active| parameter is - // added here. - const std::vector<aura::Window*>& lost() const { return lost_; } - - // Overridden from aura::client::ActivationChangeObserver: - virtual void OnWindowActivated(aura::Window* gained_active, - aura::Window* lost_active) OVERRIDE { - lost_.push_back(lost_active); - } - - private: - aura::Window* root_; - std::vector<aura::Window*> lost_; - - DISALLOW_COPY_AND_ASSIGN(RecordingActivationChangeObserver); -}; - -// ActivationChangeObserver that deletes the window losing activation. -class DeleteOnLoseActivationChangeObserver - : public aura::client::ActivationChangeObserver { - public: - explicit DeleteOnLoseActivationChangeObserver(aura::Window* window) - : root_(window->GetRootWindow()), - window_(window) { - aura::client::GetActivationClient(root_)->AddObserver(this); - } - virtual ~DeleteOnLoseActivationChangeObserver() { - aura::client::GetActivationClient(root_)->RemoveObserver(this); - } - - bool did_delete() const { return window_ == NULL; } - - // Overridden from aura::client::ActivationChangeObserver: - virtual void OnWindowActivated(aura::Window* gained_active, - aura::Window* lost_active) OVERRIDE { - if (window_ && lost_active == window_) { - window_ = NULL; - delete lost_active; - } - } - - private: - aura::Window* root_; - aura::Window* window_; - - DISALLOW_COPY_AND_ASSIGN(DeleteOnLoseActivationChangeObserver); -}; - -class ScopedFocusNotificationObserver : public FocusNotificationObserver { - public: - ScopedFocusNotificationObserver(aura::Window* root_window) - : root_window_(root_window) { - aura::client::GetActivationClient(root_window_)->AddObserver(this); - aura::client::GetFocusClient(root_window_)->AddObserver(this); - } - virtual ~ScopedFocusNotificationObserver() { - aura::client::GetActivationClient(root_window_)->RemoveObserver(this); - aura::client::GetFocusClient(root_window_)->RemoveObserver(this); - } - - private: - aura::Window* root_window_; - - DISALLOW_COPY_AND_ASSIGN(ScopedFocusNotificationObserver); -}; - -class ScopedTargetFocusNotificationObserver : public FocusNotificationObserver { - public: - ScopedTargetFocusNotificationObserver(aura::Window* root_window, int id) - : target_(root_window->GetChildById(id)) { - aura::client::SetActivationChangeObserver(target_, this); - aura::client::SetFocusChangeObserver(target_, this); - tracker_.Add(target_); - } - virtual ~ScopedTargetFocusNotificationObserver() { - if (tracker_.Contains(target_)) { - aura::client::SetActivationChangeObserver(target_, NULL); - aura::client::SetFocusChangeObserver(target_, NULL); - } - } - - private: - aura::Window* target_; - aura::WindowTracker tracker_; - - DISALLOW_COPY_AND_ASSIGN(ScopedTargetFocusNotificationObserver); -}; - -class FocusShiftingActivationObserver - : public aura::client::ActivationChangeObserver { - public: - explicit FocusShiftingActivationObserver(aura::Window* activated_window) - : activated_window_(activated_window), - shift_focus_to_(NULL) {} - virtual ~FocusShiftingActivationObserver() {} - - void set_shift_focus_to(aura::Window* shift_focus_to) { - shift_focus_to_ = shift_focus_to; - } - - private: - // Overridden from aura::client::ActivationChangeObserver: - virtual void OnWindowActivated(aura::Window* gained_active, - aura::Window* lost_active) OVERRIDE { - // Shift focus to a child. This should prevent the default focusing from - // occurring in FocusController::FocusWindow(). - if (gained_active == activated_window_) { - aura::client::FocusClient* client = - aura::client::GetFocusClient(gained_active); - client->FocusWindow(shift_focus_to_); - } - } - - aura::Window* activated_window_; - aura::Window* shift_focus_to_; - - DISALLOW_COPY_AND_ASSIGN(FocusShiftingActivationObserver); -}; - -// BaseFocusRules subclass that allows basic overrides of focus/activation to -// be tested. This is intended more as a test that the override system works at -// all, rather than as an exhaustive set of use cases, those should be covered -// in tests for those FocusRules implementations. -class TestFocusRules : public BaseFocusRules { - public: - TestFocusRules() : focus_restriction_(NULL) {} - - // Restricts focus and activation to this window and its child hierarchy. - void set_focus_restriction(aura::Window* focus_restriction) { - focus_restriction_ = focus_restriction; - } - - // Overridden from BaseFocusRules: - virtual bool SupportsChildActivation(aura::Window* window) const OVERRIDE { - // In FocusControllerTests, only the RootWindow has activatable children. - return window->GetRootWindow() == window; - } - virtual bool CanActivateWindow(aura::Window* window) const OVERRIDE { - // Restricting focus to a non-activatable child window means the activatable - // parent outside the focus restriction is activatable. - bool can_activate = - CanFocusOrActivate(window) || window->Contains(focus_restriction_); - return can_activate ? BaseFocusRules::CanActivateWindow(window) : false; - } - virtual bool CanFocusWindow(aura::Window* window) const OVERRIDE { - return CanFocusOrActivate(window) ? - BaseFocusRules::CanFocusWindow(window) : false; - } - virtual aura::Window* GetActivatableWindow( - aura::Window* window) const OVERRIDE { - return BaseFocusRules::GetActivatableWindow( - CanFocusOrActivate(window) ? window : focus_restriction_); - } - virtual aura::Window* GetFocusableWindow( - aura::Window* window) const OVERRIDE { - return BaseFocusRules::GetFocusableWindow( - CanFocusOrActivate(window) ? window : focus_restriction_); - } - virtual aura::Window* GetNextActivatableWindow( - aura::Window* ignore) const OVERRIDE { - aura::Window* next_activatable = - BaseFocusRules::GetNextActivatableWindow(ignore); - return CanFocusOrActivate(next_activatable) ? - next_activatable : GetActivatableWindow(focus_restriction_); - } - - private: - bool CanFocusOrActivate(aura::Window* window) const { - return !focus_restriction_ || focus_restriction_->Contains(window); - } - - aura::Window* focus_restriction_; - - DISALLOW_COPY_AND_ASSIGN(TestFocusRules); -}; - -// Common infrastructure shared by all FocusController test types. -class FocusControllerTestBase : public aura::test::AuraTestBase { - protected: - FocusControllerTestBase() {} - - // Overridden from aura::test::AuraTestBase: - virtual void SetUp() OVERRIDE { - // FocusController registers itself as an Env observer so it can catch all - // window initializations, including the root_window()'s, so we create it - // before allowing the base setup. - test_focus_rules_ = new TestFocusRules; - focus_controller_.reset(new FocusController(test_focus_rules_)); - aura::test::AuraTestBase::SetUp(); - root_window()->AddPreTargetHandler(focus_controller_.get()); - aura::client::SetFocusClient(root_window(), focus_controller_.get()); - aura::client::SetActivationClient(root_window(), focus_controller_.get()); - - // Hierarchy used by all tests: - // root_window - // +-- w1 - // | +-- w11 - // | +-- w12 - // +-- w2 - // | +-- w21 - // | +-- w211 - // +-- w3 - aura::Window* w1 = aura::test::CreateTestWindowWithDelegate( - aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 1, - gfx::Rect(0, 0, 50, 50), root_window()); - aura::test::CreateTestWindowWithDelegate( - aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 11, - gfx::Rect(5, 5, 10, 10), w1); - aura::test::CreateTestWindowWithDelegate( - aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 12, - gfx::Rect(15, 15, 10, 10), w1); - aura::Window* w2 = aura::test::CreateTestWindowWithDelegate( - aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 2, - gfx::Rect(75, 75, 50, 50), root_window()); - aura::Window* w21 = aura::test::CreateTestWindowWithDelegate( - aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 21, - gfx::Rect(5, 5, 10, 10), w2); - aura::test::CreateTestWindowWithDelegate( - aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 211, - gfx::Rect(1, 1, 5, 5), w21); - aura::test::CreateTestWindowWithDelegate( - aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 3, - gfx::Rect(125, 125, 50, 50), root_window()); - } - virtual void TearDown() OVERRIDE { - root_window()->RemovePreTargetHandler(focus_controller_.get()); - aura::test::AuraTestBase::TearDown(); - test_focus_rules_ = NULL; // Owned by FocusController. - focus_controller_.reset(); - } - - void FocusWindow(aura::Window* window) { - aura::client::GetFocusClient(root_window())->FocusWindow(window); - } - aura::Window* GetFocusedWindow() { - return aura::client::GetFocusClient(root_window())->GetFocusedWindow(); - } - int GetFocusedWindowId() { - aura::Window* focused_window = GetFocusedWindow(); - return focused_window ? focused_window->id() : -1; - } - void ActivateWindow(aura::Window* window) { - aura::client::GetActivationClient(root_window())->ActivateWindow(window); - } - void DeactivateWindow(aura::Window* window) { - aura::client::GetActivationClient(root_window())->DeactivateWindow(window); - } - aura::Window* GetActiveWindow() { - return aura::client::GetActivationClient(root_window())->GetActiveWindow(); - } - int GetActiveWindowId() { - aura::Window* active_window = GetActiveWindow(); - return active_window ? active_window->id() : -1; - } - - TestFocusRules* test_focus_rules() { return test_focus_rules_; } - - // Test functions. - virtual void BasicFocus() = 0; - virtual void BasicActivation() = 0; - virtual void FocusEvents() = 0; - virtual void DuplicateFocusEvents() {} - virtual void ActivationEvents() = 0; - virtual void ReactivationEvents() {} - virtual void DuplicateActivationEvents() {} - virtual void ShiftFocusWithinActiveWindow() {} - virtual void ShiftFocusToChildOfInactiveWindow() {} - virtual void ShiftFocusToParentOfFocusedWindow() {} - virtual void FocusRulesOverride() = 0; - virtual void ActivationRulesOverride() = 0; - virtual void ShiftFocusOnActivation() {} - virtual void ShiftFocusOnActivationDueToHide() {} - virtual void NoShiftActiveOnActivation() {} - virtual void NoFocusChangeOnClickOnCaptureWindow() {} - virtual void ChangeFocusWhenNothingFocusedAndCaptured() {} - virtual void DontPassDeletedWindow() {} - - private: - scoped_ptr<FocusController> focus_controller_; - TestFocusRules* test_focus_rules_; - - DISALLOW_COPY_AND_ASSIGN(FocusControllerTestBase); -}; - -// Test base for tests where focus is directly set to a target window. -class FocusControllerDirectTestBase : public FocusControllerTestBase { - protected: - FocusControllerDirectTestBase() {} - - // Different test types shift focus in different ways. - virtual void FocusWindowDirect(aura::Window* window) = 0; - virtual void ActivateWindowDirect(aura::Window* window) = 0; - virtual void DeactivateWindowDirect(aura::Window* window) = 0; - - // Input events do not change focus if the window can not be focused. - virtual bool IsInputEvent() = 0; - - void FocusWindowById(int id) { - aura::Window* window = root_window()->GetChildById(id); - DCHECK(window); - FocusWindowDirect(window); - } - void ActivateWindowById(int id) { - aura::Window* window = root_window()->GetChildById(id); - DCHECK(window); - ActivateWindowDirect(window); - } - - // Overridden from FocusControllerTestBase: - virtual void BasicFocus() OVERRIDE { - EXPECT_EQ(NULL, GetFocusedWindow()); - FocusWindowById(1); - EXPECT_EQ(1, GetFocusedWindowId()); - FocusWindowById(2); - EXPECT_EQ(2, GetFocusedWindowId()); - } - virtual void BasicActivation() OVERRIDE { - EXPECT_EQ(NULL, GetActiveWindow()); - ActivateWindowById(1); - EXPECT_EQ(1, GetActiveWindowId()); - ActivateWindowById(2); - EXPECT_EQ(2, GetActiveWindowId()); - // Verify that attempting to deactivate NULL does not crash and does not - // change activation. - DeactivateWindow(NULL); - EXPECT_EQ(2, GetActiveWindowId()); - DeactivateWindow(GetActiveWindow()); - EXPECT_EQ(1, GetActiveWindowId()); - } - virtual void FocusEvents() OVERRIDE { - ScopedFocusNotificationObserver root_observer(root_window()); - ScopedTargetFocusNotificationObserver observer1(root_window(), 1); - ScopedTargetFocusNotificationObserver observer2(root_window(), 2); - - root_observer.ExpectCounts(0, 0); - observer1.ExpectCounts(0, 0); - observer2.ExpectCounts(0, 0); - - FocusWindowById(1); - root_observer.ExpectCounts(1, 1); - observer1.ExpectCounts(1, 1); - observer2.ExpectCounts(0, 0); - - FocusWindowById(2); - root_observer.ExpectCounts(2, 2); - observer1.ExpectCounts(2, 2); - observer2.ExpectCounts(1, 1); - } - virtual void DuplicateFocusEvents() OVERRIDE { - // Focusing an existing focused window should not resend focus events. - ScopedFocusNotificationObserver root_observer(root_window()); - ScopedTargetFocusNotificationObserver observer1(root_window(), 1); - - root_observer.ExpectCounts(0, 0); - observer1.ExpectCounts(0, 0); - - FocusWindowById(1); - root_observer.ExpectCounts(1, 1); - observer1.ExpectCounts(1, 1); - - FocusWindowById(1); - root_observer.ExpectCounts(1, 1); - observer1.ExpectCounts(1, 1); - } - virtual void ActivationEvents() OVERRIDE { - ActivateWindowById(1); - - ScopedFocusNotificationObserver root_observer(root_window()); - ScopedTargetFocusNotificationObserver observer1(root_window(), 1); - ScopedTargetFocusNotificationObserver observer2(root_window(), 2); - - root_observer.ExpectCounts(0, 0); - observer1.ExpectCounts(0, 0); - observer2.ExpectCounts(0, 0); - - ActivateWindowById(2); - root_observer.ExpectCounts(1, 1); - observer1.ExpectCounts(1, 1); - observer2.ExpectCounts(1, 1); - } - virtual void ReactivationEvents() OVERRIDE { - ActivateWindowById(1); - ScopedFocusNotificationObserver root_observer(root_window()); - EXPECT_EQ(0, root_observer.reactivation_count()); - root_window()->GetChildById(2)->Hide(); - // When we attempt to activate "2", which cannot be activated because it - // is not visible, "1" will be reactivated. - ActivateWindowById(2); - EXPECT_EQ(1, root_observer.reactivation_count()); - EXPECT_EQ(root_window()->GetChildById(2), - root_observer.reactivation_requested_window()); - EXPECT_EQ(root_window()->GetChildById(1), - root_observer.reactivation_actual_window()); - } - virtual void DuplicateActivationEvents() OVERRIDE { - // Activating an existing active window should not resend activation events. - ActivateWindowById(1); - - ScopedFocusNotificationObserver root_observer(root_window()); - ScopedTargetFocusNotificationObserver observer1(root_window(), 1); - ScopedTargetFocusNotificationObserver observer2(root_window(), 2); - - root_observer.ExpectCounts(0, 0); - observer1.ExpectCounts(0, 0); - observer2.ExpectCounts(0, 0); - - ActivateWindowById(2); - root_observer.ExpectCounts(1, 1); - observer1.ExpectCounts(1, 1); - observer2.ExpectCounts(1, 1); - - ActivateWindowById(2); - root_observer.ExpectCounts(1, 1); - observer1.ExpectCounts(1, 1); - observer2.ExpectCounts(1, 1); - } - virtual void ShiftFocusWithinActiveWindow() OVERRIDE { - ActivateWindowById(1); - EXPECT_EQ(1, GetActiveWindowId()); - EXPECT_EQ(1, GetFocusedWindowId()); - FocusWindowById(11); - EXPECT_EQ(11, GetFocusedWindowId()); - FocusWindowById(12); - EXPECT_EQ(12, GetFocusedWindowId()); - } - virtual void ShiftFocusToChildOfInactiveWindow() OVERRIDE { - ActivateWindowById(2); - EXPECT_EQ(2, GetActiveWindowId()); - EXPECT_EQ(2, GetFocusedWindowId()); - FocusWindowById(11); - EXPECT_EQ(1, GetActiveWindowId()); - EXPECT_EQ(11, GetFocusedWindowId()); - } - virtual void ShiftFocusToParentOfFocusedWindow() OVERRIDE { - ActivateWindowById(1); - EXPECT_EQ(1, GetFocusedWindowId()); - FocusWindowById(11); - EXPECT_EQ(11, GetFocusedWindowId()); - FocusWindowById(1); - // Focus should _not_ shift to the parent of the already-focused window. - EXPECT_EQ(11, GetFocusedWindowId()); - } - virtual void FocusRulesOverride() OVERRIDE { - EXPECT_EQ(NULL, GetFocusedWindow()); - FocusWindowById(11); - EXPECT_EQ(11, GetFocusedWindowId()); - - test_focus_rules()->set_focus_restriction(root_window()->GetChildById(211)); - FocusWindowById(12); - // Input events leave focus unchanged; direct API calls will change focus - // to the restricted window. - int focused_window = IsInputEvent() ? 11 : 211; - EXPECT_EQ(focused_window, GetFocusedWindowId()); - - test_focus_rules()->set_focus_restriction(NULL); - FocusWindowById(12); - EXPECT_EQ(12, GetFocusedWindowId()); - } - virtual void ActivationRulesOverride() OVERRIDE { - ActivateWindowById(1); - EXPECT_EQ(1, GetActiveWindowId()); - EXPECT_EQ(1, GetFocusedWindowId()); - - aura::Window* w3 = root_window()->GetChildById(3); - test_focus_rules()->set_focus_restriction(w3); - - ActivateWindowById(2); - // Input events leave activation unchanged; direct API calls will activate - // the restricted window. - int active_window = IsInputEvent() ? 1 : 3; - EXPECT_EQ(active_window, GetActiveWindowId()); - EXPECT_EQ(active_window, GetFocusedWindowId()); - - test_focus_rules()->set_focus_restriction(NULL); - ActivateWindowById(2); - EXPECT_EQ(2, GetActiveWindowId()); - EXPECT_EQ(2, GetFocusedWindowId()); - } - virtual void ShiftFocusOnActivation() OVERRIDE { - // When a window is activated, by default that window is also focused. - // An ActivationChangeObserver may shift focus to another window within the - // same activatable window. - ActivateWindowById(2); - EXPECT_EQ(2, GetFocusedWindowId()); - ActivateWindowById(1); - EXPECT_EQ(1, GetFocusedWindowId()); - - ActivateWindowById(2); - - aura::Window* target = root_window()->GetChildById(1); - aura::client::ActivationClient* client = - aura::client::GetActivationClient(root_window()); - - scoped_ptr<FocusShiftingActivationObserver> observer( - new FocusShiftingActivationObserver(target)); - observer->set_shift_focus_to(target->GetChildById(11)); - client->AddObserver(observer.get()); - - ActivateWindowById(1); - - // w1's ActivationChangeObserver shifted focus to this child, pre-empting - // FocusController's default setting. - EXPECT_EQ(11, GetFocusedWindowId()); - - ActivateWindowById(2); - EXPECT_EQ(2, GetFocusedWindowId()); - - // Simulate a focus reset by the ActivationChangeObserver. This should - // trigger the default setting in FocusController. - observer->set_shift_focus_to(NULL); - ActivateWindowById(1); - EXPECT_EQ(1, GetFocusedWindowId()); - - client->RemoveObserver(observer.get()); - - ActivateWindowById(2); - EXPECT_EQ(2, GetFocusedWindowId()); - ActivateWindowById(1); - EXPECT_EQ(1, GetFocusedWindowId()); - } - virtual void ShiftFocusOnActivationDueToHide() OVERRIDE { - // Similar to ShiftFocusOnActivation except the activation change is - // triggered by hiding the active window. - ActivateWindowById(1); - EXPECT_EQ(1, GetFocusedWindowId()); - - // Removes window 3 as candidate for next activatable window. - root_window()->GetChildById(3)->Hide(); - EXPECT_EQ(1, GetFocusedWindowId()); - - aura::Window* target = root_window()->GetChildById(2); - aura::client::ActivationClient* client = - aura::client::GetActivationClient(root_window()); - - scoped_ptr<FocusShiftingActivationObserver> observer( - new FocusShiftingActivationObserver(target)); - observer->set_shift_focus_to(target->GetChildById(21)); - client->AddObserver(observer.get()); - - // Hide the active window. - root_window()->GetChildById(1)->Hide(); - - EXPECT_EQ(21, GetFocusedWindowId()); - - client->RemoveObserver(observer.get()); - } - virtual void NoShiftActiveOnActivation() OVERRIDE { - // When a window is activated, we need to prevent any change to activation - // from being made in response to an activation change notification. - } - - virtual void NoFocusChangeOnClickOnCaptureWindow() OVERRIDE { - scoped_ptr<aura::client::DefaultCaptureClient> capture_client( - new aura::client::DefaultCaptureClient(root_window())); - // Clicking on a window which has capture should not cause a focus change - // to the window. This test verifies whether that is indeed the case. - ActivateWindowById(1); - - EXPECT_EQ(1, GetActiveWindowId()); - EXPECT_EQ(1, GetFocusedWindowId()); - - aura::Window* w2 = root_window()->GetChildById(2); - aura::client::GetCaptureClient(root_window())->SetCapture(w2); - aura::test::EventGenerator generator(root_window(), w2); - generator.ClickLeftButton(); - - EXPECT_EQ(1, GetActiveWindowId()); - EXPECT_EQ(1, GetFocusedWindowId()); - aura::client::GetCaptureClient(root_window())->ReleaseCapture(w2); - } - - // Verifies focus change is honored while capture held. - virtual void ChangeFocusWhenNothingFocusedAndCaptured() OVERRIDE { - scoped_ptr<aura::client::DefaultCaptureClient> capture_client( - new aura::client::DefaultCaptureClient(root_window())); - aura::Window* w1 = root_window()->GetChildById(1); - aura::client::GetCaptureClient(root_window())->SetCapture(w1); - - EXPECT_EQ(-1, GetActiveWindowId()); - EXPECT_EQ(-1, GetFocusedWindowId()); - - FocusWindowById(1); - - EXPECT_EQ(1, GetActiveWindowId()); - EXPECT_EQ(1, GetFocusedWindowId()); - - aura::client::GetCaptureClient(root_window())->ReleaseCapture(w1); - } - - // Verfies if a window that loses activation is deleted during observer - // notification we don't pass the deleted window to other observers. - virtual void DontPassDeletedWindow() OVERRIDE { - FocusWindowById(1); - - EXPECT_EQ(1, GetActiveWindowId()); - EXPECT_EQ(1, GetFocusedWindowId()); - - DeleteOnLoseActivationChangeObserver observer1( - root_window()->GetChildById(1)); - RecordingActivationChangeObserver observer2(root_window()); - - FocusWindowById(2); - - EXPECT_EQ(2, GetActiveWindowId()); - EXPECT_EQ(2, GetFocusedWindowId()); - - EXPECT_TRUE(observer1.did_delete()); - ASSERT_EQ(1u, observer2.lost().size()); - EXPECT_TRUE(observer2.lost()[0] == NULL); - } - - private: - DISALLOW_COPY_AND_ASSIGN(FocusControllerDirectTestBase); -}; - -// Focus and Activation changes via aura::client::ActivationClient API. -class FocusControllerApiTest : public FocusControllerDirectTestBase { - public: - FocusControllerApiTest() {} - - private: - // Overridden from FocusControllerTestBase: - virtual void FocusWindowDirect(aura::Window* window) OVERRIDE { - FocusWindow(window); - } - virtual void ActivateWindowDirect(aura::Window* window) OVERRIDE { - ActivateWindow(window); - } - virtual void DeactivateWindowDirect(aura::Window* window) OVERRIDE { - DeactivateWindow(window); - } - virtual bool IsInputEvent() OVERRIDE { return false; } - - DISALLOW_COPY_AND_ASSIGN(FocusControllerApiTest); -}; - -// Focus and Activation changes via input events. -class FocusControllerMouseEventTest : public FocusControllerDirectTestBase { - public: - FocusControllerMouseEventTest() {} - - private: - // Overridden from FocusControllerTestBase: - virtual void FocusWindowDirect(aura::Window* window) OVERRIDE { - aura::test::EventGenerator generator(root_window(), window); - generator.ClickLeftButton(); - } - virtual void ActivateWindowDirect(aura::Window* window) OVERRIDE { - aura::test::EventGenerator generator(root_window(), window); - generator.ClickLeftButton(); - } - virtual void DeactivateWindowDirect(aura::Window* window) OVERRIDE { - aura::Window* next_activatable = - test_focus_rules()->GetNextActivatableWindow(window); - aura::test::EventGenerator generator(root_window(), next_activatable); - generator.ClickLeftButton(); - } - virtual bool IsInputEvent() OVERRIDE { return true; } - - DISALLOW_COPY_AND_ASSIGN(FocusControllerMouseEventTest); -}; - -class FocusControllerGestureEventTest : public FocusControllerDirectTestBase { - public: - FocusControllerGestureEventTest() {} - - private: - // Overridden from FocusControllerTestBase: - virtual void FocusWindowDirect(aura::Window* window) OVERRIDE { - aura::test::EventGenerator generator(root_window(), window); - generator.GestureTapAt(window->bounds().CenterPoint()); - } - virtual void ActivateWindowDirect(aura::Window* window) OVERRIDE { - aura::test::EventGenerator generator(root_window(), window); - generator.GestureTapAt(window->bounds().CenterPoint()); - } - virtual void DeactivateWindowDirect(aura::Window* window) OVERRIDE { - aura::Window* next_activatable = - test_focus_rules()->GetNextActivatableWindow(window); - aura::test::EventGenerator generator(root_window(), next_activatable); - generator.GestureTapAt(window->bounds().CenterPoint()); - } - virtual bool IsInputEvent() OVERRIDE { return true; } - - DISALLOW_COPY_AND_ASSIGN(FocusControllerGestureEventTest); -}; - -// Test base for tests where focus is implicitly set to a window as the result -// of a disposition change to the focused window or the hierarchy that contains -// it. -class FocusControllerImplicitTestBase : public FocusControllerTestBase { - protected: - explicit FocusControllerImplicitTestBase(bool parent) : parent_(parent) {} - - aura::Window* GetDispositionWindow(aura::Window* window) { - return parent_ ? window->parent() : window; - } - - // Change the disposition of |window| in such a way as it will lose focus. - virtual void ChangeWindowDisposition(aura::Window* window) = 0; - - // Allow each disposition change test to add additional post-disposition - // change expectations. - virtual void PostDispostionChangeExpectations() {} - - // Overridden from FocusControllerTestBase: - virtual void BasicFocus() OVERRIDE { - EXPECT_EQ(NULL, GetFocusedWindow()); - - aura::Window* w211 = root_window()->GetChildById(211); - FocusWindow(w211); - EXPECT_EQ(211, GetFocusedWindowId()); - - ChangeWindowDisposition(w211); - // BasicFocusRules passes focus to the parent. - EXPECT_EQ(parent_ ? 2 : 21, GetFocusedWindowId()); - } - virtual void BasicActivation() OVERRIDE { - DCHECK(!parent_) << "Activation tests don't support parent changes."; - - EXPECT_EQ(NULL, GetActiveWindow()); - - aura::Window* w2 = root_window()->GetChildById(2); - ActivateWindow(w2); - EXPECT_EQ(2, GetActiveWindowId()); - - ChangeWindowDisposition(w2); - EXPECT_EQ(3, GetActiveWindowId()); - PostDispostionChangeExpectations(); - } - virtual void FocusEvents() OVERRIDE { - aura::Window* w211 = root_window()->GetChildById(211); - FocusWindow(w211); - - ScopedFocusNotificationObserver root_observer(root_window()); - ScopedTargetFocusNotificationObserver observer211(root_window(), 211); - root_observer.ExpectCounts(0, 0); - observer211.ExpectCounts(0, 0); - - ChangeWindowDisposition(w211); - root_observer.ExpectCounts(0, 1); - observer211.ExpectCounts(0, 1); - } - virtual void ActivationEvents() OVERRIDE { - DCHECK(!parent_) << "Activation tests don't support parent changes."; - - aura::Window* w2 = root_window()->GetChildById(2); - ActivateWindow(w2); - - ScopedFocusNotificationObserver root_observer(root_window()); - ScopedTargetFocusNotificationObserver observer2(root_window(), 2); - ScopedTargetFocusNotificationObserver observer3(root_window(), 3); - root_observer.ExpectCounts(0, 0); - observer2.ExpectCounts(0, 0); - observer3.ExpectCounts(0, 0); - - ChangeWindowDisposition(w2); - root_observer.ExpectCounts(1, 1); - observer2.ExpectCounts(1, 1); - observer3.ExpectCounts(1, 1); - } - virtual void FocusRulesOverride() OVERRIDE { - EXPECT_EQ(NULL, GetFocusedWindow()); - aura::Window* w211 = root_window()->GetChildById(211); - FocusWindow(w211); - EXPECT_EQ(211, GetFocusedWindowId()); - - test_focus_rules()->set_focus_restriction(root_window()->GetChildById(11)); - ChangeWindowDisposition(w211); - // Normally, focus would shift to the parent (w21) but the override shifts - // it to 11. - EXPECT_EQ(11, GetFocusedWindowId()); - - test_focus_rules()->set_focus_restriction(NULL); - } - virtual void ActivationRulesOverride() OVERRIDE { - DCHECK(!parent_) << "Activation tests don't support parent changes."; - - aura::Window* w1 = root_window()->GetChildById(1); - ActivateWindow(w1); - - EXPECT_EQ(1, GetActiveWindowId()); - EXPECT_EQ(1, GetFocusedWindowId()); - - aura::Window* w3 = root_window()->GetChildById(3); - test_focus_rules()->set_focus_restriction(w3); - - // Normally, activation/focus would move to w2, but since we have a focus - // restriction, it should move to w3 instead. - ChangeWindowDisposition(w1); - EXPECT_EQ(3, GetActiveWindowId()); - EXPECT_EQ(3, GetFocusedWindowId()); - - test_focus_rules()->set_focus_restriction(NULL); - ActivateWindow(root_window()->GetChildById(2)); - EXPECT_EQ(2, GetActiveWindowId()); - EXPECT_EQ(2, GetFocusedWindowId()); - } - - private: - // When true, the disposition change occurs to the parent of the window - // instead of to the window. This verifies that changes occurring in the - // hierarchy that contains the window affect the window's focus. - bool parent_; - - DISALLOW_COPY_AND_ASSIGN(FocusControllerImplicitTestBase); -}; - -// Focus and Activation changes in response to window visibility changes. -class FocusControllerHideTest : public FocusControllerImplicitTestBase { - public: - FocusControllerHideTest() : FocusControllerImplicitTestBase(false) {} - - protected: - FocusControllerHideTest(bool parent) - : FocusControllerImplicitTestBase(parent) {} - - // Overridden from FocusControllerImplicitTestBase: - virtual void ChangeWindowDisposition(aura::Window* window) OVERRIDE { - GetDispositionWindow(window)->Hide(); - } - virtual void PostDispostionChangeExpectations() OVERRIDE { - // BasicActivation() starts with the stacking order: 1, 2, 3 (3 topmost) - // and then activates 2. After 2 is hidden in ChangeWindowDisposition - // above, 3 is activated, but code in - // FocusController::OnWindowVisibilityChanging keeps 2's layer above 3's - // until a hide animation completes (e.g. a fade-out transition). - aura::Window* w2 = root_window()->GetChildById(2); - aura::Window* w3 = root_window()->GetChildById(3); - - // W2 was hidden, but its layer should still be stacked above W3's. - typedef std::vector<ui::Layer*> Layers; - const Layers& children = w3->parent()->layer()->children(); - Layers::const_iterator w3_iter = - std::find(children.begin(), children.end(), w3->layer()); - Layers::const_iterator w2_iter = - std::find(children.begin(), children.end(), w2->layer()); - EXPECT_TRUE(w2_iter > w3_iter); - } - - private: - DISALLOW_COPY_AND_ASSIGN(FocusControllerHideTest); -}; - -// Focus and Activation changes in response to window parent visibility -// changes. -class FocusControllerParentHideTest : public FocusControllerHideTest { - public: - FocusControllerParentHideTest() : FocusControllerHideTest(true) {} - - private: - DISALLOW_COPY_AND_ASSIGN(FocusControllerParentHideTest); -}; - -// Focus and Activation changes in response to window destruction. -class FocusControllerDestructionTest : public FocusControllerImplicitTestBase { - public: - FocusControllerDestructionTest() : FocusControllerImplicitTestBase(false) {} - - protected: - FocusControllerDestructionTest(bool parent) - : FocusControllerImplicitTestBase(parent) {} - - // Overridden from FocusControllerImplicitTestBase: - virtual void ChangeWindowDisposition(aura::Window* window) OVERRIDE { - delete GetDispositionWindow(window); - } - - private: - DISALLOW_COPY_AND_ASSIGN(FocusControllerDestructionTest); -}; - -// Focus and Activation changes in response to window parent destruction. -class FocusControllerParentDestructionTest - : public FocusControllerDestructionTest { - public: - FocusControllerParentDestructionTest() - : FocusControllerDestructionTest(true) {} - - private: - DISALLOW_COPY_AND_ASSIGN(FocusControllerParentDestructionTest); -}; - -// Focus and Activation changes in response to window removal. -class FocusControllerRemovalTest : public FocusControllerImplicitTestBase { - public: - FocusControllerRemovalTest() : FocusControllerImplicitTestBase(false) {} - - protected: - FocusControllerRemovalTest(bool parent) - : FocusControllerImplicitTestBase(parent) {} - - // Overridden from FocusControllerImplicitTestBase: - virtual void ChangeWindowDisposition(aura::Window* window) OVERRIDE { - aura::Window* disposition_window = GetDispositionWindow(window); - disposition_window->parent()->RemoveChild(disposition_window); - window_owner_.reset(disposition_window); - } - virtual void TearDown() OVERRIDE { - window_owner_.reset(); - FocusControllerImplicitTestBase::TearDown(); - } - - private: - scoped_ptr<aura::Window> window_owner_; - - DISALLOW_COPY_AND_ASSIGN(FocusControllerRemovalTest); -}; - -// Focus and Activation changes in response to window parent removal. -class FocusControllerParentRemovalTest : public FocusControllerRemovalTest { - public: - FocusControllerParentRemovalTest() : FocusControllerRemovalTest(true) {} - - private: - DISALLOW_COPY_AND_ASSIGN(FocusControllerParentRemovalTest); -}; - - -#define FOCUS_CONTROLLER_TEST(TESTCLASS, TESTNAME) \ - TEST_F(TESTCLASS, TESTNAME) { TESTNAME(); } - -// Runs direct focus change tests (input events and API calls). -#define DIRECT_FOCUS_CHANGE_TESTS(TESTNAME) \ - FOCUS_CONTROLLER_TEST(FocusControllerApiTest, TESTNAME) \ - FOCUS_CONTROLLER_TEST(FocusControllerMouseEventTest, TESTNAME) \ - FOCUS_CONTROLLER_TEST(FocusControllerGestureEventTest, TESTNAME) - -// Runs implicit focus change tests for disposition changes to target. -#define IMPLICIT_FOCUS_CHANGE_TARGET_TESTS(TESTNAME) \ - FOCUS_CONTROLLER_TEST(FocusControllerHideTest, TESTNAME) \ - FOCUS_CONTROLLER_TEST(FocusControllerDestructionTest, TESTNAME) \ - FOCUS_CONTROLLER_TEST(FocusControllerRemovalTest, TESTNAME) - -// Runs implicit focus change tests for disposition changes to target's parent -// hierarchy. -#define IMPLICIT_FOCUS_CHANGE_PARENT_TESTS(TESTNAME) \ - /* TODO(beng): parent destruction tests are not supported at - present due to workspace manager issues. \ - FOCUS_CONTROLLER_TEST(FocusControllerParentDestructionTest, TESTNAME) */ \ - FOCUS_CONTROLLER_TEST(FocusControllerParentHideTest, TESTNAME) \ - FOCUS_CONTROLLER_TEST(FocusControllerParentRemovalTest, TESTNAME) - -// Runs all implicit focus change tests (changes to the target and target's -// parent hierarchy) -#define IMPLICIT_FOCUS_CHANGE_TESTS(TESTNAME) \ - IMPLICIT_FOCUS_CHANGE_TARGET_TESTS(TESTNAME) \ - IMPLICIT_FOCUS_CHANGE_PARENT_TESTS(TESTNAME) - -// Runs all possible focus change tests. -#define ALL_FOCUS_TESTS(TESTNAME) \ - DIRECT_FOCUS_CHANGE_TESTS(TESTNAME) \ - IMPLICIT_FOCUS_CHANGE_TESTS(TESTNAME) - -// Runs focus change tests that apply only to the target. For example, -// implicit activation changes caused by window disposition changes do not -// occur when changes to the containing hierarchy happen. -#define TARGET_FOCUS_TESTS(TESTNAME) \ - DIRECT_FOCUS_CHANGE_TESTS(TESTNAME) \ - IMPLICIT_FOCUS_CHANGE_TARGET_TESTS(TESTNAME) - -// - Focuses a window, verifies that focus changed. -ALL_FOCUS_TESTS(BasicFocus); - -// - Activates a window, verifies that activation changed. -TARGET_FOCUS_TESTS(BasicActivation); - -// - Focuses a window, verifies that focus events were dispatched. -ALL_FOCUS_TESTS(FocusEvents); - -// - Focuses or activates a window multiple times, verifies that events are only -// dispatched when focus/activation actually changes. -DIRECT_FOCUS_CHANGE_TESTS(DuplicateFocusEvents); -DIRECT_FOCUS_CHANGE_TESTS(DuplicateActivationEvents); - -// - Activates a window, verifies that activation events were dispatched. -TARGET_FOCUS_TESTS(ActivationEvents); - -// - Attempts to active a hidden window, verifies that current window is -// attempted to be reactivated and the appropriate event dispatched. -FOCUS_CONTROLLER_TEST(FocusControllerApiTest, ReactivationEvents); - -// - Input events/API calls shift focus between focusable windows within the -// active window. -DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusWithinActiveWindow); - -// - Input events/API calls to a child window of an inactive window shifts -// activation to the activatable parent and focuses the child. -DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusToChildOfInactiveWindow); - -// - Input events/API calls to focus the parent of the focused window do not -// shift focus away from the child. -DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusToParentOfFocusedWindow); - -// - Verifies that FocusRules determine what can be focused. -ALL_FOCUS_TESTS(FocusRulesOverride); - -// - Verifies that FocusRules determine what can be activated. -TARGET_FOCUS_TESTS(ActivationRulesOverride); - -// - Verifies that attempts to change focus or activation from a focus or -// activation change observer are ignored. -DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusOnActivation); -DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusOnActivationDueToHide); -DIRECT_FOCUS_CHANGE_TESTS(NoShiftActiveOnActivation); - -// Clicking on a window which has capture should not result in a focus change. -DIRECT_FOCUS_CHANGE_TESTS(NoFocusChangeOnClickOnCaptureWindow); - -FOCUS_CONTROLLER_TEST(FocusControllerApiTest, - ChangeFocusWhenNothingFocusedAndCaptured); - -// See description above DontPassDeletedWindow() for details. -FOCUS_CONTROLLER_TEST(FocusControllerApiTest, DontPassDeletedWindow); - -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/focus_rules.h b/chromium/ui/views/corewm/focus_rules.h deleted file mode 100644 index 559a2385432..00000000000 --- a/chromium/ui/views/corewm/focus_rules.h +++ /dev/null @@ -1,68 +0,0 @@ -// 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_COREWM_FOCUS_RULES_H_ -#define UI_VIEWS_COREWM_FOCUS_RULES_H_ - -#include "ui/views/views_export.h" - -namespace aura { -class Window; -} - -namespace views { -namespace corewm { - -// Implemented by an object that establishes the rules about what can be -// focused or activated. -class VIEWS_EXPORT FocusRules { - public: - virtual ~FocusRules() {} - - // Returns true if |window| is a toplevel window. Whether or not a window - // is considered toplevel is determined by a similar set of rules that - // govern activation and focus. Not all toplevel windows are activatable, - // call CanActivateWindow() to determine if a window can be activated. - virtual bool IsToplevelWindow(aura::Window* window) const = 0; - // Returns true if |window| can be activated or focused. - virtual bool CanActivateWindow(aura::Window* window) const = 0; - // For CanFocusWindow(), NULL is supported, because NULL is a valid focusable - // window (in the case of clearing focus). - virtual bool CanFocusWindow(aura::Window* window) const = 0; - - // Returns the toplevel window containing |window|. Not all toplevel windows - // are activatable, call GetActivatableWindow() instead to return the - // activatable window, which might be in a different hierarchy. - // Will return NULL if |window| is not contained by a window considered to be - // a toplevel window. - virtual aura::Window* GetToplevelWindow(aura::Window* window) const = 0; - // Returns the activatable or focusable window given an attempt to activate or - // focus |window|. Some possible scenarios (not intended to be exhaustive): - // - |window| is a child of a non-focusable window and so focus must be set - // according to rules defined by the delegate, e.g. to a parent. - // - |window| is an activatable window that is the transient parent of a modal - // window, so attempts to activate |window| should result in the modal - // transient being activated instead. - // These methods may return NULL if they are unable to find an activatable - // or focusable window given |window|. - virtual aura::Window* GetActivatableWindow(aura::Window* window) const = 0; - virtual aura::Window* GetFocusableWindow(aura::Window* window) const = 0; - - // Returns the next window to activate in the event that |ignore| is no longer - // activatable. This function is called when something is happening to - // |ignore| that means it can no longer have focus or activation, including - // but not limited to: - // - it or its parent hierarchy is being hidden, or removed from the - // RootWindow. - // - it is being destroyed. - // - it is being explicitly deactivated. - // |ignore| cannot be NULL. - virtual aura::Window* GetNextActivatableWindow( - aura::Window* ignore) const = 0; -}; - -} // namespace corewm -} // namespace views - -#endif // UI_VIEWS_COREWM_FOCUS_RULES_H_ diff --git a/chromium/ui/views/corewm/image_grid.cc b/chromium/ui/views/corewm/image_grid.cc deleted file mode 100644 index 29c775773c7..00000000000 --- a/chromium/ui/views/corewm/image_grid.cc +++ /dev/null @@ -1,308 +0,0 @@ -// 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/corewm/image_grid.h" - -#include <algorithm> - -#include "third_party/skia/include/core/SkColor.h" -#include "third_party/skia/include/core/SkXfermode.h" -#include "ui/compositor/dip_util.h" -#include "ui/gfx/canvas.h" -#include "ui/gfx/image/image.h" -#include "ui/gfx/rect.h" -#include "ui/gfx/rect_conversions.h" -#include "ui/gfx/size.h" -#include "ui/gfx/size_conversions.h" -#include "ui/gfx/transform.h" - -using std::max; -using std::min; - -namespace views { -namespace corewm { - -gfx::RectF ImageGrid::TestAPI::GetTransformedLayerBounds( - const ui::Layer& layer) { - gfx::RectF bounds = layer.bounds(); - layer.transform().TransformRect(&bounds); - return bounds; -} - -ImageGrid::ImageGrid() - : layer_(new ui::Layer(ui::LAYER_NOT_DRAWN)), - top_image_height_(0), - bottom_image_height_(0), - left_image_width_(0), - right_image_width_(0), - base_top_row_height_(0), - base_bottom_row_height_(0), - base_left_column_width_(0), - base_right_column_width_(0) { -} - -ImageGrid::~ImageGrid() { -} - -void ImageGrid::SetImages(const gfx::Image* top_left_image, - const gfx::Image* top_image, - const gfx::Image* top_right_image, - const gfx::Image* left_image, - const gfx::Image* center_image, - const gfx::Image* right_image, - const gfx::Image* bottom_left_image, - const gfx::Image* bottom_image, - const gfx::Image* bottom_right_image) { - SetImage(top_left_image, &top_left_layer_, &top_left_painter_); - SetImage(top_image, &top_layer_, &top_painter_); - SetImage(top_right_image, &top_right_layer_, &top_right_painter_); - SetImage(left_image, &left_layer_, &left_painter_); - SetImage(center_image, ¢er_layer_, ¢er_painter_); - SetImage(right_image, &right_layer_, &right_painter_); - SetImage(bottom_left_image, &bottom_left_layer_, &bottom_left_painter_); - SetImage(bottom_image, &bottom_layer_, &bottom_painter_); - SetImage(bottom_right_image, &bottom_right_layer_, &bottom_right_painter_); - - top_image_height_ = GetImageSize(top_image).height(); - bottom_image_height_ = GetImageSize(bottom_image).height(); - left_image_width_ = GetImageSize(left_image).width(); - right_image_width_ = GetImageSize(right_image).width(); - - base_top_row_height_ = max(GetImageSize(top_left_image).height(), - max(GetImageSize(top_image).height(), - GetImageSize(top_right_image).height())); - base_bottom_row_height_ = max(GetImageSize(bottom_left_image).height(), - max(GetImageSize(bottom_image).height(), - GetImageSize(bottom_right_image).height())); - base_left_column_width_ = max(GetImageSize(top_left_image).width(), - max(GetImageSize(left_image).width(), - GetImageSize(bottom_left_image).width())); - base_right_column_width_ = max(GetImageSize(top_right_image).width(), - max(GetImageSize(right_image).width(), - GetImageSize(bottom_right_image).width())); - - // Invalidate previous |size_| so calls to SetSize() will recompute it. - size_.SetSize(0, 0); -} - -void ImageGrid::SetSize(const gfx::Size& size) { - if (size_ == size) - return; - - size_ = size; - - gfx::Rect updated_bounds = layer_->bounds(); - updated_bounds.set_size(size); - layer_->SetBounds(updated_bounds); - - // Calculate the available amount of space for corner images on all sides of - // the grid. If the images don't fit, we need to clip them. - const int left = min(base_left_column_width_, size_.width() / 2); - const int right = min(base_right_column_width_, size_.width() - left); - const int top = min(base_top_row_height_, size_.height() / 2); - const int bottom = min(base_bottom_row_height_, size_.height() - top); - - // The remaining space goes to the center image. - int center_width = std::max(size.width() - left - right, 0); - int center_height = std::max(size.height() - top - bottom, 0); - - // At non-integer scale factors, the ratio of dimensions in DIP is not - // necessarily the same as the ratio in physical pixels due to rounding. Set - // the transform on each of the layers based on dimensions in pixels. - gfx::Size center_size_in_pixels = gfx::ToFlooredSize(gfx::ScaleSize( - gfx::Size(center_width, center_height), layer_->device_scale_factor())); - - if (top_layer_.get()) { - if (center_width > 0) { - gfx::Transform transform; - transform.Translate(left, 0); - ScaleWidth(center_size_in_pixels, top_layer_.get(), transform); - top_layer_->SetTransform(transform); - } - top_layer_->SetVisible(center_width > 0); - } - if (bottom_layer_.get()) { - if (center_width > 0) { - gfx::Transform transform; - transform.Translate( - left, size.height() - bottom_layer_->bounds().height()); - ScaleWidth(center_size_in_pixels, bottom_layer_.get(), transform); - bottom_layer_->SetTransform(transform); - } - bottom_layer_->SetVisible(center_width > 0); - } - if (left_layer_.get()) { - if (center_height > 0) { - gfx::Transform transform; - transform.Translate(0, top); - ScaleHeight(center_size_in_pixels, left_layer_.get(), transform); - left_layer_->SetTransform(transform); - } - left_layer_->SetVisible(center_height > 0); - } - if (right_layer_.get()) { - if (center_height > 0) { - gfx::Transform transform; - transform.Translate( - size.width() - right_layer_->bounds().width(), top); - ScaleHeight(center_size_in_pixels, right_layer_.get(), transform); - right_layer_->SetTransform(transform); - } - right_layer_->SetVisible(center_height > 0); - } - - if (top_left_layer_.get()) { - // No transformation needed; it should be at (0, 0) and unscaled. - top_left_painter_->SetClipRect( - LayerExceedsSize(top_left_layer_.get(), gfx::Size(left, top)) ? - gfx::Rect(gfx::Rect(0, 0, left, top)) : - gfx::Rect(), - top_left_layer_.get()); - } - if (top_right_layer_.get()) { - gfx::Transform transform; - transform.Translate(size.width() - top_right_layer_->bounds().width(), 0.0); - top_right_layer_->SetTransform(transform); - top_right_painter_->SetClipRect( - LayerExceedsSize(top_right_layer_.get(), gfx::Size(right, top)) ? - gfx::Rect(top_right_layer_->bounds().width() - right, 0, - right, top) : - gfx::Rect(), - top_right_layer_.get()); - } - if (bottom_left_layer_.get()) { - gfx::Transform transform; - transform.Translate( - 0.0, size.height() - bottom_left_layer_->bounds().height()); - bottom_left_layer_->SetTransform(transform); - bottom_left_painter_->SetClipRect( - LayerExceedsSize(bottom_left_layer_.get(), gfx::Size(left, bottom)) ? - gfx::Rect(0, bottom_left_layer_->bounds().height() - bottom, - left, bottom) : - gfx::Rect(), - bottom_left_layer_.get()); - } - if (bottom_right_layer_.get()) { - gfx::Transform transform; - transform.Translate( - size.width() - bottom_right_layer_->bounds().width(), - size.height() - bottom_right_layer_->bounds().height()); - bottom_right_layer_->SetTransform(transform); - bottom_right_painter_->SetClipRect( - LayerExceedsSize(bottom_right_layer_.get(), gfx::Size(right, bottom)) ? - gfx::Rect(bottom_right_layer_->bounds().width() - right, - bottom_right_layer_->bounds().height() - bottom, - right, bottom) : - gfx::Rect(), - bottom_right_layer_.get()); - } - - if (center_layer_.get()) { - if (center_width > 0 && center_height > 0) { - gfx::Transform transform; - transform.Translate(left, top); - transform.Scale(center_width / center_layer_->bounds().width(), - center_height / center_layer_->bounds().height()); - center_layer_->SetTransform(transform); - } - center_layer_->SetVisible(center_width > 0 && center_height > 0); - } -} - -void ImageGrid::SetContentBounds(const gfx::Rect& content_bounds) { - - SetSize(gfx::Size( - content_bounds.width() + left_image_width_ + right_image_width_, - content_bounds.height() + top_image_height_ + - bottom_image_height_)); - layer_->SetBounds( - gfx::Rect(content_bounds.x() - left_image_width_, - content_bounds.y() - top_image_height_, - layer_->bounds().width(), - layer_->bounds().height())); -} - -void ImageGrid::ImagePainter::SetClipRect(const gfx::Rect& clip_rect, - ui::Layer* layer) { - if (clip_rect != clip_rect_) { - clip_rect_ = clip_rect; - layer->SchedulePaint(layer->bounds()); - } -} - -void ImageGrid::ImagePainter::OnPaintLayer(gfx::Canvas* canvas) { - if (!clip_rect_.IsEmpty()) - canvas->ClipRect(clip_rect_); - canvas->DrawImageInt(*(image_->ToImageSkia()), 0, 0); -} - -void ImageGrid::ImagePainter::OnDeviceScaleFactorChanged( - float device_scale_factor) { - // Redrawing will take care of scale factor change. -} - -base::Closure ImageGrid::ImagePainter::PrepareForLayerBoundsChange() { - return base::Closure(); -} - -// static -gfx::Size ImageGrid::GetImageSize(const gfx::Image* image) { - return image ? - gfx::Size(image->ToImageSkia()->width(), image->ToImageSkia()->height()) : - gfx::Size(); -} - -// static -bool ImageGrid::LayerExceedsSize(const ui::Layer* layer, - const gfx::Size& size) { - return layer->bounds().width() > size.width() || - layer->bounds().height() > size.height(); -} - -void ImageGrid::SetImage(const gfx::Image* image, - scoped_ptr<ui::Layer>* layer_ptr, - scoped_ptr<ImagePainter>* painter_ptr) { - // Clean out old layers and painters. - if (layer_ptr->get()) - layer_->Remove(layer_ptr->get()); - layer_ptr->reset(); - painter_ptr->reset(); - - // If we're not using an image, we're done. - if (!image) - return; - - // Set up the new layer and painter. - layer_ptr->reset(new ui::Layer(ui::LAYER_TEXTURED)); - - const gfx::Size size = GetImageSize(image); - layer_ptr->get()->SetBounds(gfx::Rect(0, 0, size.width(), size.height())); - - painter_ptr->reset(new ImagePainter(image)); - layer_ptr->get()->set_delegate(painter_ptr->get()); - layer_ptr->get()->SetFillsBoundsOpaquely(false); - layer_ptr->get()->SetVisible(true); - layer_->Add(layer_ptr->get()); -} - -void ImageGrid::ScaleWidth(gfx::Size center, - ui::Layer* layer, - gfx::Transform& transform) { - int layer_width = ConvertSizeToPixel(layer, - layer->bounds().size()).width(); - float scale = static_cast<float>(center.width()) / layer_width; - transform.Scale(scale, 1.0); -} - -void ImageGrid::ScaleHeight(gfx::Size center, - ui::Layer* layer, - gfx::Transform& transform) { - int layer_height = ConvertSizeToPixel(layer, - layer->bounds().size()).height(); - float scale = static_cast<float>(center.height()) / layer_height; - transform.Scale(1.0, scale); -} - -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/image_grid.h b/chromium/ui/views/corewm/image_grid.h deleted file mode 100644 index 51b1b56ee62..00000000000 --- a/chromium/ui/views/corewm/image_grid.h +++ /dev/null @@ -1,228 +0,0 @@ -// 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_COREWM_IMAGE_GRID_H_ -#define UI_VIEWS_COREWM_IMAGE_GRID_H_ - -#include "base/basictypes.h" -#include "base/gtest_prod_util.h" -#include "base/memory/scoped_ptr.h" -#include "ui/compositor/layer.h" -#include "ui/compositor/layer_delegate.h" -#include "ui/gfx/rect.h" -#include "ui/gfx/size.h" -#include "ui/views/views_export.h" - -namespace gfx { -class Image; -} // namespace gfx - -namespace views { -namespace corewm { - -// An ImageGrid is a 3x3 array of ui::Layers, each containing an image. -// -// As the grid is resized, its images fill the requested space: -// - corner images are not scaled -// - top and bottom images are scaled horizontally -// - left and right images are scaled vertically -// - the center image is scaled in both directions -// -// If one of the non-center images is smaller than the largest images in its -// row or column, it will be aligned with the outside of the grid. For -// example, given 4x4 top-left and top-right images and a 1x2 top images: -// -// +--------+---------------------+--------+ -// | | top | | -// | top- +---------------------+ top- + -// | left | | right | -// +----+---+ +---+----+ -// | | | | -// ... -// -// This may seem odd at first, but it lets ImageGrid be used to draw shadows -// with curved corners that extend inwards beyond a window's borders. In the -// below example, the top-left corner image is overlaid on top of the window's -// top-left corner: -// -// +---------+----------------------- -// | ..xxx|XXXXXXXXXXXXXXXXXX -// | .xXXXXX|XXXXXXXXXXXXXXXXXX_____ -// | .xXX | ^ window's top edge -// | .xXX | -// +---------+ -// | xXX| -// | xXX|< window's left edge -// | xXX| -// ... -// -class VIEWS_EXPORT ImageGrid { - public: - // Helper class for use by tests. - class VIEWS_EXPORT TestAPI { - public: - TestAPI(ImageGrid* grid) : grid_(grid) {} - - gfx::Rect top_left_clip_rect() const { - return grid_->top_left_painter_->clip_rect_; - } - gfx::Rect top_right_clip_rect() const { - return grid_->top_right_painter_->clip_rect_; - } - gfx::Rect bottom_left_clip_rect() const { - return grid_->bottom_left_painter_->clip_rect_; - } - gfx::Rect bottom_right_clip_rect() const { - return grid_->bottom_right_painter_->clip_rect_; - } - - // Returns |layer|'s bounds after applying the layer's current transform. - gfx::RectF GetTransformedLayerBounds(const ui::Layer& layer); - - private: - ImageGrid* grid_; // not owned - - DISALLOW_COPY_AND_ASSIGN(TestAPI); - }; - - ImageGrid(); - ~ImageGrid(); - - ui::Layer* layer() { return layer_.get(); } - int top_image_height() const { return top_image_height_; } - int bottom_image_height() const { return bottom_image_height_; } - int left_image_width() const { return left_image_width_; } - int right_image_width() const { return right_image_width_; } - - // Visible to allow independent layer animations and for testing. - ui::Layer* top_left_layer() const { return top_left_layer_.get(); } - ui::Layer* top_layer() const { return top_layer_.get(); } - ui::Layer* top_right_layer() const { return top_right_layer_.get(); } - ui::Layer* left_layer() const { return left_layer_.get(); } - ui::Layer* center_layer() const { return center_layer_.get(); } - ui::Layer* right_layer() const { return right_layer_.get(); } - ui::Layer* bottom_left_layer() const { return bottom_left_layer_.get(); } - ui::Layer* bottom_layer() const { return bottom_layer_.get(); } - ui::Layer* bottom_right_layer() const { return bottom_right_layer_.get(); } - - // Sets the grid to display the passed-in images (any of which can be NULL). - // Ownership of the images remains with the caller. May be called more than - // once to switch images. - void SetImages(const gfx::Image* top_left_image, - const gfx::Image* top_image, - const gfx::Image* top_right_image, - const gfx::Image* left_image, - const gfx::Image* center_image, - const gfx::Image* right_image, - const gfx::Image* bottom_left_image, - const gfx::Image* bottom_image, - const gfx::Image* bottom_right_image); - - void SetSize(const gfx::Size& size); - - // Sets the grid to a position and size such that the inner edges of the top, - // bottom, left and right images will be flush with |content_bounds_in_dip|. - void SetContentBounds(const gfx::Rect& content_bounds_in_dip); - - private: - // Delegate responsible for painting a specific image on a layer. - class ImagePainter : public ui::LayerDelegate { - public: - ImagePainter(const gfx::Image* image) : image_(image) {} - virtual ~ImagePainter() {} - - // Clips |layer| to |clip_rect|. Triggers a repaint if the clipping - // rectangle has changed. An empty rectangle disables clipping. - void SetClipRect(const gfx::Rect& clip_rect, ui::Layer* layer); - - // ui::LayerDelegate implementation: - virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE; - virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE; - virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE; - - private: - friend class TestAPI; - - const gfx::Image* image_; // not owned - - gfx::Rect clip_rect_; - - DISALLOW_COPY_AND_ASSIGN(ImagePainter); - }; - - // Returns the dimensions of |image| if non-NULL or gfx::Size(0, 0) otherwise. - static gfx::Size GetImageSize(const gfx::Image* image); - - // Returns true if |layer|'s bounds don't fit within |size|. - static bool LayerExceedsSize(const ui::Layer* layer, const gfx::Size& size); - - // Sets |layer_ptr| and |painter_ptr| to display |image| and adds the - // passed-in layer to |layer_|. If image is NULL resets |layer_ptr| and - // |painter_ptr| and removes any existing layer from |layer_|. - void SetImage(const gfx::Image* image, - scoped_ptr<ui::Layer>* layer_ptr, - scoped_ptr<ImagePainter>* painter_ptr); - - // Sets the scaling for the transform applied to a layer. The left, top, - // right and bottom layers are stretched to the height or width of the - // center image. - void ScaleWidth(gfx::Size center, - ui::Layer* layer, - gfx::Transform& transform); - void ScaleHeight(gfx::Size center, - ui::Layer* layer, - gfx::Transform& transform); - - // Layer that contains all of the image layers. - scoped_ptr<ui::Layer> layer_; - - // The grid's dimensions. - gfx::Size size_; - - // Heights and widths of the images displayed by |top_layer_|, - // |bottom_layer_|, |left_layer_|, and |right_layer_|. - int top_image_height_; - int bottom_image_height_; - int left_image_width_; - int right_image_width_; - - // Heights of the tallest images in the top and bottom rows and the widest - // images in the left and right columns. Note that we may have less actual - // space than this available if the images are large and |size_| is small. - int base_top_row_height_; - int base_bottom_row_height_; - int base_left_column_width_; - int base_right_column_width_; - - // Layers used to display the various images. Children of |layer_|. - // Positions for which no images were supplied are NULL. - scoped_ptr<ui::Layer> top_left_layer_; - scoped_ptr<ui::Layer> top_layer_; - scoped_ptr<ui::Layer> top_right_layer_; - scoped_ptr<ui::Layer> left_layer_; - scoped_ptr<ui::Layer> center_layer_; - scoped_ptr<ui::Layer> right_layer_; - scoped_ptr<ui::Layer> bottom_left_layer_; - scoped_ptr<ui::Layer> bottom_layer_; - scoped_ptr<ui::Layer> bottom_right_layer_; - - // Delegates responsible for painting the above layers. - // Positions for which no images were supplied are NULL. - scoped_ptr<ImagePainter> top_left_painter_; - scoped_ptr<ImagePainter> top_painter_; - scoped_ptr<ImagePainter> top_right_painter_; - scoped_ptr<ImagePainter> left_painter_; - scoped_ptr<ImagePainter> center_painter_; - scoped_ptr<ImagePainter> right_painter_; - scoped_ptr<ImagePainter> bottom_left_painter_; - scoped_ptr<ImagePainter> bottom_painter_; - scoped_ptr<ImagePainter> bottom_right_painter_; - - DISALLOW_COPY_AND_ASSIGN(ImageGrid); -}; - -} // namespace corewm -} // namespace views - -#endif // UI_VIEWS_COREWM_IMAGE_GRID_H_ diff --git a/chromium/ui/views/corewm/image_grid_unittest.cc b/chromium/ui/views/corewm/image_grid_unittest.cc deleted file mode 100644 index 4b5508d733d..00000000000 --- a/chromium/ui/views/corewm/image_grid_unittest.cc +++ /dev/null @@ -1,341 +0,0 @@ -// 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 "third_party/skia/include/core/SkBitmap.h" -#include "ui/views/test/views_test_base.h" -#include "ui/gfx/image/image.h" -#include "ui/gfx/image/image_skia.h" -#include "ui/views/corewm/image_grid.h" - -namespace views { -namespace corewm { - -namespace { - -// Creates a gfx::Image with the requested dimensions. -gfx::Image* CreateImage(const gfx::Size& size) { - SkBitmap bitmap; - bitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height()); - return new gfx::Image(gfx::ImageSkia::CreateFrom1xBitmap(bitmap)); -} - -} // namespace - -typedef ViewsTestBase ImageGridTest; - -// Test that an ImageGrid's layers are transformed correctly when SetSize() is -// called. -TEST_F(ImageGridTest, Basic) { - // Size of the images around the grid's border. - const int kBorder = 2; - - scoped_ptr<gfx::Image> image_1x1(CreateImage(gfx::Size(1, 1))); - scoped_ptr<gfx::Image> image_1xB(CreateImage(gfx::Size(1, kBorder))); - scoped_ptr<gfx::Image> image_Bx1(CreateImage(gfx::Size(kBorder, 1))); - scoped_ptr<gfx::Image> image_BxB(CreateImage(gfx::Size(kBorder, kBorder))); - - ImageGrid grid; - grid.SetImages(image_BxB.get(), image_1xB.get(), image_BxB.get(), - image_Bx1.get(), image_1x1.get(), image_Bx1.get(), - image_BxB.get(), image_1xB.get(), image_BxB.get()); - - ImageGrid::TestAPI test_api(&grid); - ASSERT_TRUE(grid.top_left_layer() != NULL); - ASSERT_TRUE(grid.top_layer() != NULL); - ASSERT_TRUE(grid.top_right_layer() != NULL); - ASSERT_TRUE(grid.left_layer() != NULL); - ASSERT_TRUE(grid.center_layer() != NULL); - ASSERT_TRUE(grid.right_layer() != NULL); - ASSERT_TRUE(grid.bottom_left_layer() != NULL); - ASSERT_TRUE(grid.bottom_layer() != NULL); - ASSERT_TRUE(grid.bottom_right_layer() != NULL); - - const gfx::Size size(20, 30); - grid.SetSize(size); - - // The top-left layer should be flush with the top-left corner and unscaled. - EXPECT_EQ(gfx::RectF(0, 0, kBorder, kBorder).ToString(), - test_api.GetTransformedLayerBounds( - *grid.top_left_layer()).ToString()); - - // The top layer should be flush with the top edge and stretched horizontally - // between the two top corners. - EXPECT_EQ(gfx::RectF( - kBorder, 0, size.width() - 2 * kBorder, kBorder).ToString(), - test_api.GetTransformedLayerBounds( - *grid.top_layer()).ToString()); - - // The top-right layer should be flush with the top-right corner and unscaled. - EXPECT_EQ(gfx::RectF(size.width() - kBorder, 0, kBorder, kBorder).ToString(), - test_api.GetTransformedLayerBounds( - *grid.top_right_layer()).ToString()); - - // The left layer should be flush with the left edge and stretched vertically - // between the two left corners. - EXPECT_EQ(gfx::RectF( - 0, kBorder, kBorder, size.height() - 2 * kBorder).ToString(), - test_api.GetTransformedLayerBounds( - *grid.left_layer()).ToString()); - - // The center layer should fill the space in the middle of the grid. - EXPECT_EQ(gfx::RectF( - kBorder, kBorder, size.width() - 2 * kBorder, - size.height() - 2 * kBorder).ToString(), - test_api.GetTransformedLayerBounds( - *grid.center_layer()).ToString()); - - // The right layer should be flush with the right edge and stretched - // vertically between the two right corners. - EXPECT_EQ(gfx::RectF( - size.width() - kBorder, kBorder, - kBorder, size.height() - 2 * kBorder).ToString(), - test_api.GetTransformedLayerBounds( - *grid.right_layer()).ToString()); - - // The bottom-left layer should be flush with the bottom-left corner and - // unscaled. - EXPECT_EQ(gfx::RectF(0, size.height() - kBorder, kBorder, kBorder).ToString(), - test_api.GetTransformedLayerBounds( - *grid.bottom_left_layer()).ToString()); - - // The bottom layer should be flush with the bottom edge and stretched - // horizontally between the two bottom corners. - EXPECT_EQ(gfx::RectF( - kBorder, size.height() - kBorder, - size.width() - 2 * kBorder, kBorder).ToString(), - test_api.GetTransformedLayerBounds( - *grid.bottom_layer()).ToString()); - - // The bottom-right layer should be flush with the bottom-right corner and - // unscaled. - EXPECT_EQ(gfx::RectF( - size.width() - kBorder, size.height() - kBorder, - kBorder, kBorder).ToString(), - test_api.GetTransformedLayerBounds( - *grid.bottom_right_layer()).ToString()); -} - -// Test that an ImageGrid's layers are transformed correctly when -// SetContentBounds() is called. -TEST_F(ImageGridTest, SetContentBounds) { - // Size of the images around the grid's border. - const int kBorder = 2; - - scoped_ptr<gfx::Image> image_1x1(CreateImage(gfx::Size(1, 1))); - scoped_ptr<gfx::Image> image_1xB(CreateImage(gfx::Size(1, kBorder))); - scoped_ptr<gfx::Image> image_Bx1(CreateImage(gfx::Size(kBorder, 1))); - scoped_ptr<gfx::Image> image_BxB(CreateImage(gfx::Size(kBorder, kBorder))); - - ImageGrid grid; - grid.SetImages(image_BxB.get(), image_1xB.get(), image_BxB.get(), - image_Bx1.get(), image_1x1.get(), image_Bx1.get(), - image_BxB.get(), image_1xB.get(), image_BxB.get()); - - ImageGrid::TestAPI test_api(&grid); - - const gfx::Point origin(5, 10); - const gfx::Size size(20, 30); - grid.SetContentBounds(gfx::Rect(origin, size)); - - // The master layer is positioned above the top-left corner of the content - // bounds and has height/width that contain the grid and bounds. - EXPECT_EQ(gfx::RectF(origin.x() - kBorder, - origin.y() - kBorder, - size.width() + 2 * kBorder, - size.height() + 2 * kBorder).ToString(), - test_api.GetTransformedLayerBounds(*grid.layer()).ToString()); -} - -// Check that we don't crash if only a single image is supplied. -TEST_F(ImageGridTest, SingleImage) { - const int kBorder = 1; - scoped_ptr<gfx::Image> image(CreateImage(gfx::Size(kBorder, kBorder))); - - ImageGrid grid; - grid.SetImages(NULL, image.get(), NULL, - NULL, NULL, NULL, - NULL, NULL, NULL); - - ImageGrid::TestAPI test_api(&grid); - EXPECT_TRUE(grid.top_left_layer() == NULL); - ASSERT_TRUE(grid.top_layer() != NULL); - EXPECT_TRUE(grid.top_right_layer() == NULL); - EXPECT_TRUE(grid.left_layer() == NULL); - EXPECT_TRUE(grid.center_layer() == NULL); - EXPECT_TRUE(grid.right_layer() == NULL); - EXPECT_TRUE(grid.bottom_left_layer() == NULL); - EXPECT_TRUE(grid.bottom_layer() == NULL); - EXPECT_TRUE(grid.bottom_right_layer() == NULL); - - const gfx::Size kSize(10, 10); - grid.SetSize(kSize); - - // The top layer should be scaled horizontally across the entire width, but it - // shouldn't be scaled vertically. - EXPECT_EQ(gfx::RectF(0, 0, kSize.width(), kBorder).ToString(), - test_api.GetTransformedLayerBounds( - *grid.top_layer()).ToString()); -} - -// Check that we don't crash when we reset existing images to NULL and -// reset NULL images to new ones. -TEST_F(ImageGridTest, ResetImages) { - const int kBorder = 1; - scoped_ptr<gfx::Image> image(CreateImage(gfx::Size(kBorder, kBorder))); - - ImageGrid grid; - grid.SetImages(NULL, image.get(), NULL, - NULL, NULL, NULL, - NULL, NULL, NULL); - - // Only the top edge has a layer. - ImageGrid::TestAPI test_api(&grid); - ASSERT_TRUE(grid.top_left_layer() == NULL); - ASSERT_FALSE(grid.top_layer() == NULL); - ASSERT_TRUE(grid.top_right_layer() == NULL); - ASSERT_TRUE(grid.left_layer() == NULL); - ASSERT_TRUE(grid.center_layer() == NULL); - ASSERT_TRUE(grid.right_layer() == NULL); - ASSERT_TRUE(grid.bottom_left_layer() == NULL); - ASSERT_TRUE(grid.bottom_layer() == NULL); - ASSERT_TRUE(grid.bottom_right_layer() == NULL); - - grid.SetImages(NULL, NULL, NULL, - NULL, NULL, NULL, - NULL, image.get(), NULL); - - // Now only the bottom edge has a layer. - ASSERT_TRUE(grid.top_left_layer() == NULL); - ASSERT_TRUE(grid.top_layer() == NULL); - ASSERT_TRUE(grid.top_right_layer() == NULL); - ASSERT_TRUE(grid.left_layer() == NULL); - ASSERT_TRUE(grid.center_layer() == NULL); - ASSERT_TRUE(grid.right_layer() == NULL); - ASSERT_TRUE(grid.bottom_left_layer() == NULL); - ASSERT_FALSE(grid.bottom_layer() == NULL); - ASSERT_TRUE(grid.bottom_right_layer() == NULL); -} - -// Test that side (top, left, right, bottom) layers that are narrower than their -// adjacent corner layers stay pinned to the outside edges instead of getting -// moved inwards or scaled. This exercises the scenario used for shadows. -TEST_F(ImageGridTest, SmallerSides) { - const int kCorner = 2; - const int kEdge = 1; - - scoped_ptr<gfx::Image> top_left_image( - CreateImage(gfx::Size(kCorner, kCorner))); - scoped_ptr<gfx::Image> top_image(CreateImage(gfx::Size(kEdge, kEdge))); - scoped_ptr<gfx::Image> top_right_image( - CreateImage(gfx::Size(kCorner, kCorner))); - scoped_ptr<gfx::Image> left_image(CreateImage(gfx::Size(kEdge, kEdge))); - scoped_ptr<gfx::Image> right_image(CreateImage(gfx::Size(kEdge, kEdge))); - - ImageGrid grid; - grid.SetImages(top_left_image.get(), top_image.get(), top_right_image.get(), - left_image.get(), NULL, right_image.get(), - NULL, NULL, NULL); - ImageGrid::TestAPI test_api(&grid); - - const gfx::Size kSize(20, 30); - grid.SetSize(kSize); - - // The top layer should be flush with the top edge and stretched horizontally - // between the two top corners. - EXPECT_EQ(gfx::RectF( - kCorner, 0, kSize.width() - 2 * kCorner, kEdge).ToString(), - test_api.GetTransformedLayerBounds( - *grid.top_layer()).ToString()); - - // The left layer should be flush with the left edge and stretched vertically - // between the top left corner and the bottom. - EXPECT_EQ(gfx::RectF( - 0, kCorner, kEdge, kSize.height() - kCorner).ToString(), - test_api.GetTransformedLayerBounds( - *grid.left_layer()).ToString()); - - // The right layer should be flush with the right edge and stretched - // vertically between the top right corner and the bottom. - EXPECT_EQ(gfx::RectF( - kSize.width() - kEdge, kCorner, - kEdge, kSize.height() - kCorner).ToString(), - test_api.GetTransformedLayerBounds( - *grid.right_layer()).ToString()); -} - -// Test that we hide or clip layers as needed when the grid is assigned a small -// size. -TEST_F(ImageGridTest, TooSmall) { - const int kCorner = 5; - const int kCenter = 3; - const int kEdge = 3; - - scoped_ptr<gfx::Image> top_left_image( - CreateImage(gfx::Size(kCorner, kCorner))); - scoped_ptr<gfx::Image> top_image(CreateImage(gfx::Size(kEdge, kEdge))); - scoped_ptr<gfx::Image> top_right_image( - CreateImage(gfx::Size(kCorner, kCorner))); - scoped_ptr<gfx::Image> left_image(CreateImage(gfx::Size(kEdge, kEdge))); - scoped_ptr<gfx::Image> center_image(CreateImage(gfx::Size(kCenter, kCenter))); - scoped_ptr<gfx::Image> right_image(CreateImage(gfx::Size(kEdge, kEdge))); - scoped_ptr<gfx::Image> bottom_left_image( - CreateImage(gfx::Size(kCorner, kCorner))); - scoped_ptr<gfx::Image> bottom_image(CreateImage(gfx::Size(kEdge, kEdge))); - scoped_ptr<gfx::Image> bottom_right_image( - CreateImage(gfx::Size(kCorner, kCorner))); - - ImageGrid grid; - grid.SetImages( - top_left_image.get(), top_image.get(), top_right_image.get(), - left_image.get(), center_image.get(), right_image.get(), - bottom_left_image.get(), bottom_image.get(), bottom_right_image.get()); - ImageGrid::TestAPI test_api(&grid); - - // Set a size that's smaller than the combined (unscaled) corner images. - const gfx::Size kSmallSize(kCorner + kCorner - 3, kCorner + kCorner - 5); - grid.SetSize(kSmallSize); - - // The scalable images around the sides and in the center should be hidden. - EXPECT_FALSE(grid.top_layer()->visible()); - EXPECT_FALSE(grid.bottom_layer()->visible()); - EXPECT_FALSE(grid.left_layer()->visible()); - EXPECT_FALSE(grid.right_layer()->visible()); - EXPECT_FALSE(grid.center_layer()->visible()); - - // The corner images' clip rects should sum to the expected size. - EXPECT_EQ(kSmallSize.width(), - test_api.top_left_clip_rect().width() + - test_api.top_right_clip_rect().width()); - EXPECT_EQ(kSmallSize.width(), - test_api.bottom_left_clip_rect().width() + - test_api.bottom_right_clip_rect().width()); - EXPECT_EQ(kSmallSize.height(), - test_api.top_left_clip_rect().height() + - test_api.bottom_left_clip_rect().height()); - EXPECT_EQ(kSmallSize.height(), - test_api.top_right_clip_rect().height() + - test_api.bottom_right_clip_rect().height()); - - // Resize the grid to be large enough to show all images. - const gfx::Size kLargeSize(kCorner + kCorner + kCenter, - kCorner + kCorner + kCenter); - grid.SetSize(kLargeSize); - - // The scalable images should be visible now. - EXPECT_TRUE(grid.top_layer()->visible()); - EXPECT_TRUE(grid.bottom_layer()->visible()); - EXPECT_TRUE(grid.left_layer()->visible()); - EXPECT_TRUE(grid.right_layer()->visible()); - EXPECT_TRUE(grid.center_layer()->visible()); - - // We shouldn't be clipping the corner images anymore. - EXPECT_TRUE(test_api.top_left_clip_rect().IsEmpty()); - EXPECT_TRUE(test_api.top_right_clip_rect().IsEmpty()); - EXPECT_TRUE(test_api.bottom_left_clip_rect().IsEmpty()); - EXPECT_TRUE(test_api.bottom_right_clip_rect().IsEmpty()); -} - -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/input_method_event_filter.cc b/chromium/ui/views/corewm/input_method_event_filter.cc deleted file mode 100644 index 8e9ead8e1d9..00000000000 --- a/chromium/ui/views/corewm/input_method_event_filter.cc +++ /dev/null @@ -1,81 +0,0 @@ -// 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/corewm/input_method_event_filter.h" - -#include "ui/aura/client/aura_constants.h" -#include "ui/aura/root_window.h" -#include "ui/base/ime/input_method.h" -#include "ui/base/ime/input_method_factory.h" -#include "ui/events/event.h" - -namespace views { -namespace corewm { - -//////////////////////////////////////////////////////////////////////////////// -// InputMethodEventFilter, public: - -InputMethodEventFilter::InputMethodEventFilter(gfx::AcceleratedWidget widget) - : input_method_(ui::CreateInputMethod(this, widget)), - target_dispatcher_(NULL) { - // TODO(yusukes): Check if the root window is currently focused and pass the - // result to Init(). - input_method_->Init(true); -} - -InputMethodEventFilter::~InputMethodEventFilter() { -} - -void InputMethodEventFilter::SetInputMethodPropertyInRootWindow( - aura::Window* root_window) { - root_window->SetProperty(aura::client::kRootWindowInputMethodKey, - input_method_.get()); -} - -//////////////////////////////////////////////////////////////////////////////// -// InputMethodEventFilter, EventFilter implementation: - -void InputMethodEventFilter::OnKeyEvent(ui::KeyEvent* event) { - const ui::EventType type = event->type(); - if (type == ui::ET_TRANSLATED_KEY_PRESS || - type == ui::ET_TRANSLATED_KEY_RELEASE) { - // The |event| is already handled by this object, change the type of the - // event to ui::ET_KEY_* and pass it to the next filter. - static_cast<ui::TranslatedKeyEvent*>(event)->ConvertToKeyEvent(); - } else { - // If the focused window is changed, all requests to IME will be - // discarded so it's safe to update the target_dispatcher_ here. - aura::Window* target = static_cast<aura::Window*>(event->target()); - target_dispatcher_ = target->GetRootWindow()->GetDispatcher(); - DCHECK(target_dispatcher_); - if (input_method_->DispatchKeyEvent(*event)) - event->StopPropagation(); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// InputMethodEventFilter, ui::InputMethodDelegate implementation: - -bool InputMethodEventFilter::DispatchKeyEventPostIME( - const base::NativeEvent& event) { -#if defined(OS_WIN) - DCHECK(event.message != WM_CHAR); -#endif - ui::TranslatedKeyEvent aura_event(event, false /* is_char */); - return target_dispatcher_->AsRootWindowHostDelegate()->OnHostKeyEvent( - &aura_event); -} - -bool InputMethodEventFilter::DispatchFabricatedKeyEventPostIME( - ui::EventType type, - ui::KeyboardCode key_code, - int flags) { - ui::TranslatedKeyEvent aura_event(type == ui::ET_KEY_PRESSED, key_code, - flags); - return target_dispatcher_->AsRootWindowHostDelegate()->OnHostKeyEvent( - &aura_event); -} - -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/input_method_event_filter.h b/chromium/ui/views/corewm/input_method_event_filter.h deleted file mode 100644 index 902244c8865..00000000000 --- a/chromium/ui/views/corewm/input_method_event_filter.h +++ /dev/null @@ -1,58 +0,0 @@ -// 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_COREWM_INPUT_METHOD_EVENT_FILTER_H_ -#define UI_VIEWS_COREWM_INPUT_METHOD_EVENT_FILTER_H_ - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/memory/scoped_ptr.h" -#include "ui/aura/window.h" -#include "ui/base/ime/input_method_delegate.h" -#include "ui/events/event_handler.h" -#include "ui/gfx/native_widget_types.h" -#include "ui/views/views_export.h" - -namespace ui { -class InputMethod; -} - -namespace views { -namespace corewm { - -// An event filter that forwards a KeyEvent to a system IME, and dispatches a -// TranslatedKeyEvent to the root window as needed. -class VIEWS_EXPORT InputMethodEventFilter - : public ui::EventHandler, - public ui::internal::InputMethodDelegate { - public: - explicit InputMethodEventFilter(gfx::AcceleratedWidget widget); - virtual ~InputMethodEventFilter(); - - void SetInputMethodPropertyInRootWindow(aura::Window* root_window); - - ui::InputMethod* input_method() const { return input_method_.get(); } - - private: - // Overridden from ui::EventHandler: - virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE; - - // Overridden from ui::internal::InputMethodDelegate. - virtual bool DispatchKeyEventPostIME(const base::NativeEvent& event) OVERRIDE; - virtual bool DispatchFabricatedKeyEventPostIME(ui::EventType type, - ui::KeyboardCode key_code, - int flags) OVERRIDE; - - scoped_ptr<ui::InputMethod> input_method_; - - // The target dispatcher that will receive translated key events from the IME. - aura::WindowEventDispatcher* target_dispatcher_; - - DISALLOW_COPY_AND_ASSIGN(InputMethodEventFilter); -}; - -} // namespace corewm -} // namespace views - -#endif // UI_VIEWS_COREWM_INPUT_METHOD_EVENT_FILTER_H_ diff --git a/chromium/ui/views/corewm/input_method_event_filter_unittest.cc b/chromium/ui/views/corewm/input_method_event_filter_unittest.cc deleted file mode 100644 index 2c95dc44e82..00000000000 --- a/chromium/ui/views/corewm/input_method_event_filter_unittest.cc +++ /dev/null @@ -1,91 +0,0 @@ -// 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/corewm/input_method_event_filter.h" - -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/aura/client/activation_client.h" -#include "ui/aura/client/aura_constants.h" -#include "ui/aura/root_window.h" -#include "ui/views/corewm/compound_event_filter.h" -#include "ui/aura/test/aura_test_base.h" -#include "ui/aura/test/event_generator.h" -#include "ui/aura/test/test_event_handler.h" -#include "ui/aura/test/test_windows.h" - -#if !defined(OS_WIN) && !defined(USE_X11) -// On platforms except Windows and X11, aura::test::EventGenerator::PressKey -// generates a key event without native_event(), which is not supported by -// ui::MockInputMethod. -#define TestInputMethodKeyEventPropagation \ -DISABLED_TestInputMethodKeyEventPropagation -#endif - -namespace views { -namespace corewm { - -typedef aura::test::AuraTestBase InputMethodEventFilterTest; - -TEST_F(InputMethodEventFilterTest, TestInputMethodProperty) { - CompoundEventFilter* root_filter = new CompoundEventFilter; - root_window()->SetEventFilter(root_filter); - - InputMethodEventFilter input_method_event_filter( - dispatcher()->host()->GetAcceleratedWidget()); - root_filter->AddHandler(&input_method_event_filter); - - // Tests if InputMethodEventFilter adds a window property on its - // construction. - EXPECT_TRUE(root_window()->GetProperty( - aura::client::kRootWindowInputMethodKey)); - - root_filter->RemoveHandler(&input_method_event_filter); -} - -// Tests if InputMethodEventFilter dispatches a ui::ET_TRANSLATED_KEY_* event to -// the root window. -TEST_F(InputMethodEventFilterTest, TestInputMethodKeyEventPropagation) { - CompoundEventFilter* root_filter = new CompoundEventFilter; - root_window()->SetEventFilter(root_filter); - - // Add the InputMethodEventFilter before the TestEventFilter. - InputMethodEventFilter input_method_event_filter( - dispatcher()->host()->GetAcceleratedWidget()); - root_filter->AddHandler(&input_method_event_filter); - - // Add TestEventFilter to the RootWindow. - aura::test::TestEventHandler test_filter; - root_filter->AddHandler(&test_filter); - - // We need an active window. Otherwise, the root window will not forward a key - // event to event filters. - aura::test::TestWindowDelegate test_delegate; - scoped_ptr<aura::Window> window(aura::test::CreateTestWindowWithDelegate( - &test_delegate, - -1, - gfx::Rect(), - root_window())); - aura::client::GetActivationClient(root_window())->ActivateWindow( - window.get()); - - // Send a fake key event to the root window. InputMethodEventFilter, which is - // automatically set up by AshTestBase, consumes it and sends a new - // ui::ET_TRANSLATED_KEY_* event to the root window, which will be consumed by - // the test event filter. - aura::test::EventGenerator generator(root_window()); - EXPECT_EQ(0, test_filter.num_key_events()); - generator.PressKey(ui::VKEY_SPACE, 0); - EXPECT_EQ(1, test_filter.num_key_events()); - generator.ReleaseKey(ui::VKEY_SPACE, 0); - EXPECT_EQ(2, test_filter.num_key_events()); - - root_filter->RemoveHandler(&input_method_event_filter); - root_filter->RemoveHandler(&test_filter); - - // Reset window before |test_delegate| gets deleted. - window.reset(); -} - -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/native_cursor_manager.h b/chromium/ui/views/corewm/native_cursor_manager.h deleted file mode 100644 index 75e4c8580a5..00000000000 --- a/chromium/ui/views/corewm/native_cursor_manager.h +++ /dev/null @@ -1,67 +0,0 @@ -// 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_COREWM_NATIVE_CURSOR_MANAGER_H_ -#define UI_VIEWS_COREWM_NATIVE_CURSOR_MANAGER_H_ - -#include "base/strings/string16.h" -#include "ui/views/corewm/native_cursor_manager_delegate.h" -#include "ui/views/views_export.h" - -namespace gfx { -class Display; -} - -namespace views { -namespace corewm { - -// Interface where platforms such as Ash or Desktop aura are notified of -// requested changes to cursor state. When requested, implementer should tell -// the CursorManager of any actual state changes performed through the -// delegate. -class VIEWS_EXPORT NativeCursorManager { - public: - virtual ~NativeCursorManager() {} - - // A request to set the screen DPI. Can cause changes in the current cursor. - virtual void SetDisplay( - const gfx::Display& display, - views::corewm::NativeCursorManagerDelegate* delegate) = 0; - - // A request to set the cursor to |cursor|. At minimum, implementer should - // call NativeCursorManagerDelegate::CommitCursor() with whatever cursor is - // actually used. - virtual void SetCursor( - gfx::NativeCursor cursor, - views::corewm::NativeCursorManagerDelegate* delegate) = 0; - - // A request to set the visibility of the cursor. At minimum, implementer - // should call NativeCursorManagerDelegate::CommitVisibility() with whatever - // the visibility is. - virtual void SetVisibility( - bool visible, - views::corewm::NativeCursorManagerDelegate* delegate) = 0; - - // A request to set the scale of the cursor icon. - virtual void SetScale( - float scale, - views::corewm::NativeCursorManagerDelegate* delegate) = 0; - - // A request to set the scale of the cursor icon. - virtual void SetCursorSet( - ui::CursorSetType cursor_set, - views::corewm::NativeCursorManagerDelegate* delegate) = 0; - - // A request to set whether mouse events are disabled. At minimum, - // implementer should call NativeCursorManagerDelegate:: - // CommitMouseEventsEnabled() with whether mouse events are actually enabled. - virtual void SetMouseEventsEnabled( - bool enabled, - views::corewm::NativeCursorManagerDelegate* delegate) = 0; -}; - -} // namespace corewm -} // namespace views - -#endif // UI_VIEWS_COREWM_NATIVE_CURSOR_MANAGER_H_ diff --git a/chromium/ui/views/corewm/native_cursor_manager_delegate.h b/chromium/ui/views/corewm/native_cursor_manager_delegate.h deleted file mode 100644 index 491ab47b1b4..00000000000 --- a/chromium/ui/views/corewm/native_cursor_manager_delegate.h +++ /dev/null @@ -1,36 +0,0 @@ -// 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_COREWM_NATIVE_CURSOR_MANAGER_DELEGATE_H_ -#define UI_VIEWS_COREWM_NATIVE_CURSOR_MANAGER_DELEGATE_H_ - -#include "ui/base/cursor/cursor.h" -#include "ui/gfx/native_widget_types.h" -#include "ui/views/views_export.h" - -namespace views { -namespace corewm { - -// The non-public interface that CursorManager exposes to its users. This -// gives accessors to all the current state, and mutators to all the current -// state. -class VIEWS_EXPORT NativeCursorManagerDelegate { - public: - virtual ~NativeCursorManagerDelegate() {} - - // TODO(tdanderson): Possibly remove this interface. - virtual gfx::NativeCursor GetCursor() const = 0; - virtual bool IsCursorVisible() const = 0; - - virtual void CommitCursor(gfx::NativeCursor cursor) = 0; - virtual void CommitVisibility(bool visible) = 0; - virtual void CommitScale(float scale) = 0; - virtual void CommitCursorSet(ui::CursorSetType cursor_set) = 0; - virtual void CommitMouseEventsEnabled(bool enabled) = 0; -}; - -} // namespace corewm -} // namespace views - -#endif // UI_VIEWS_COREWM_NATIVE_CURSOR_MANAGER_DELEGATE_H_ diff --git a/chromium/ui/views/corewm/shadow.cc b/chromium/ui/views/corewm/shadow.cc deleted file mode 100644 index 4411c8d5637..00000000000 --- a/chromium/ui/views/corewm/shadow.cc +++ /dev/null @@ -1,195 +0,0 @@ -// 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/corewm/shadow.h" - -#include "grit/ui_resources.h" -#include "ui/base/resource/resource_bundle.h" -#include "ui/compositor/scoped_layer_animation_settings.h" -#include "ui/views/corewm/image_grid.h" - -namespace { - -// Shadow opacity for different styles. -const float kActiveShadowOpacity = 1.0f; -const float kInactiveShadowOpacity = 0.2f; -const float kSmallShadowOpacity = 1.0f; - -// Interior inset for different styles. -const int kActiveInteriorInset = 0; -const int kInactiveInteriorInset = 0; -const int kSmallInteriorInset = 5; - -// Duration for opacity animation in milliseconds. -const int kShadowAnimationDurationMs = 100; - -float GetOpacityForStyle(views::corewm::Shadow::Style style) { - switch (style) { - case views::corewm::Shadow::STYLE_ACTIVE: - return kActiveShadowOpacity; - case views::corewm::Shadow::STYLE_INACTIVE: - return kInactiveShadowOpacity; - case views::corewm::Shadow::STYLE_SMALL: - return kSmallShadowOpacity; - } - return 1.0f; -} - -int GetInteriorInsetForStyle(views::corewm::Shadow::Style style) { - switch (style) { - case views::corewm::Shadow::STYLE_ACTIVE: - return kActiveInteriorInset; - case views::corewm::Shadow::STYLE_INACTIVE: - return kInactiveInteriorInset; - case views::corewm::Shadow::STYLE_SMALL: - return kSmallInteriorInset; - } - return 0; -} - -} // namespace - -namespace views { -namespace corewm { - -Shadow::Shadow() : style_(STYLE_ACTIVE), interior_inset_(0) { -} - -Shadow::~Shadow() { -} - -void Shadow::Init(Style style) { - style_ = style; - image_grid_.reset(new ImageGrid); - UpdateImagesForStyle(); - image_grid_->layer()->set_name("Shadow"); - image_grid_->layer()->SetOpacity(GetOpacityForStyle(style_)); -} - -void Shadow::SetContentBounds(const gfx::Rect& content_bounds) { - content_bounds_ = content_bounds; - UpdateImageGridBounds(); -} - -ui::Layer* Shadow::layer() const { - return image_grid_->layer(); -} - -void Shadow::SetStyle(Style style) { - if (style_ == style) - return; - - Style old_style = style_; - style_ = style; - - // Stop waiting for any as yet unfinished implicit animations. - StopObservingImplicitAnimations(); - - // If we're switching to or from the small style, don't bother with - // animations. - if (style == STYLE_SMALL || old_style == STYLE_SMALL) { - UpdateImagesForStyle(); - image_grid_->layer()->SetOpacity(GetOpacityForStyle(style)); - return; - } - - // If we're becoming active, switch images now. Because the inactive image - // has a very low opacity the switch isn't noticeable and this approach - // allows us to use only a single set of shadow images at a time. - if (style == STYLE_ACTIVE) { - UpdateImagesForStyle(); - // Opacity was baked into inactive image, start opacity low to match. - image_grid_->layer()->SetOpacity(kInactiveShadowOpacity); - } - - { - // Property sets within this scope will be implicitly animated. - ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator()); - settings.AddObserver(this); - settings.SetTransitionDuration( - base::TimeDelta::FromMilliseconds(kShadowAnimationDurationMs)); - switch (style_) { - case STYLE_ACTIVE: - image_grid_->layer()->SetOpacity(kActiveShadowOpacity); - break; - case STYLE_INACTIVE: - image_grid_->layer()->SetOpacity(kInactiveShadowOpacity); - break; - default: - NOTREACHED() << "Unhandled style " << style_; - break; - } - } -} - -void Shadow::OnImplicitAnimationsCompleted() { - // If we just finished going inactive, switch images. This doesn't cause - // a visual pop because the inactive image opacity is so low. - if (style_ == STYLE_INACTIVE) { - UpdateImagesForStyle(); - // Opacity is baked into inactive image, so set fully opaque. - image_grid_->layer()->SetOpacity(1.0f); - } -} - -void Shadow::UpdateImagesForStyle() { - ResourceBundle& res = ResourceBundle::GetSharedInstance(); - switch (style_) { - case STYLE_ACTIVE: - image_grid_->SetImages( - &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_TOP_LEFT), - &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_TOP), - &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_TOP_RIGHT), - &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_LEFT), - NULL, - &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_RIGHT), - &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_BOTTOM_LEFT), - &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_BOTTOM), - &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_BOTTOM_RIGHT)); - break; - case STYLE_INACTIVE: - image_grid_->SetImages( - &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_TOP_LEFT), - &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_TOP), - &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_TOP_RIGHT), - &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_LEFT), - NULL, - &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_RIGHT), - &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_BOTTOM_LEFT), - &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_BOTTOM), - &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_BOTTOM_RIGHT)); - break; - case STYLE_SMALL: - image_grid_->SetImages( - &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_TOP_LEFT), - &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_TOP), - &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_TOP_RIGHT), - &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_LEFT), - NULL, - &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_RIGHT), - &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_BOTTOM_LEFT), - &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_BOTTOM), - &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_BOTTOM_RIGHT)); - break; - default: - NOTREACHED() << "Unhandled style " << style_; - break; - } - - // Update interior inset for style. - interior_inset_ = GetInteriorInsetForStyle(style_); - - // Image sizes may have changed. - UpdateImageGridBounds(); -} - -void Shadow::UpdateImageGridBounds() { - // Update bounds based on content bounds and image sizes. - gfx::Rect image_grid_bounds = content_bounds_; - image_grid_bounds.Inset(interior_inset_, interior_inset_); - image_grid_->SetContentBounds(image_grid_bounds); -} - -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/shadow.h b/chromium/ui/views/corewm/shadow.h deleted file mode 100644 index 794ba3dbeaa..00000000000 --- a/chromium/ui/views/corewm/shadow.h +++ /dev/null @@ -1,88 +0,0 @@ -// 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_COREWM_SHADOW_H_ -#define UI_VIEWS_COREWM_SHADOW_H_ - -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "ui/compositor/layer_animation_observer.h" -#include "ui/gfx/rect.h" -#include "ui/views/views_export.h" - -namespace ui { -class Layer; -} // namespace ui - -namespace views { -namespace corewm { - -class ImageGrid; - -// Simple class that draws a drop shadow around content at given bounds. -class VIEWS_EXPORT Shadow : public ui::ImplicitAnimationObserver { - public: - enum Style { - // Active windows have more opaque shadows, shifted down to make the window - // appear "higher". - STYLE_ACTIVE, - - // Inactive windows have less opaque shadows. - STYLE_INACTIVE, - - // Small windows like tooltips and context menus have lighter, smaller - // shadows. - STYLE_SMALL, - }; - - Shadow(); - virtual ~Shadow(); - - void Init(Style style); - - // Returns |image_grid_|'s ui::Layer. This is exposed so it can be added to - // the same layer as the content and stacked below it. SetContentBounds() - // should be used to adjust the shadow's size and position (rather than - // applying transformations to this layer). - ui::Layer* layer() const; - - const gfx::Rect& content_bounds() const { return content_bounds_; } - Style style() const { return style_; } - - // Moves and resizes |image_grid_| to frame |content_bounds|. - void SetContentBounds(const gfx::Rect& content_bounds); - - // Sets the shadow's style, animating opacity as necessary. - void SetStyle(Style style); - - // ui::ImplicitAnimationObserver overrides: - virtual void OnImplicitAnimationsCompleted() OVERRIDE; - - private: - // Updates the |image_grid_| images to the current |style_|. - void UpdateImagesForStyle(); - - // Updates the |image_grid_| bounds based on its image sizes and the - // current |content_bounds_|. - void UpdateImageGridBounds(); - - // The current style, set when the transition animation starts. - Style style_; - - scoped_ptr<ImageGrid> image_grid_; - - // Bounds of the content that the shadow encloses. - gfx::Rect content_bounds_; - - // The interior inset of the shadow images. The content bounds of the image - // grid should be set to |content_bounds_| inset by this amount. - int interior_inset_; - - DISALLOW_COPY_AND_ASSIGN(Shadow); -}; - -} // namespace corewm -} // namespace views - -#endif // UI_VIEWS_COREWM_SHADOW_H_ diff --git a/chromium/ui/views/corewm/shadow_controller.cc b/chromium/ui/views/corewm/shadow_controller.cc deleted file mode 100644 index 49307d8e2d2..00000000000 --- a/chromium/ui/views/corewm/shadow_controller.cc +++ /dev/null @@ -1,273 +0,0 @@ -// 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/corewm/shadow_controller.h" - -#include <utility> - -#include "base/command_line.h" -#include "base/logging.h" -#include "base/memory/linked_ptr.h" -#include "base/scoped_observer.h" -#include "ui/aura/client/activation_client.h" -#include "ui/aura/env.h" -#include "ui/aura/env_observer.h" -#include "ui/aura/root_window.h" -#include "ui/aura/window.h" -#include "ui/aura/window_observer.h" -#include "ui/compositor/layer.h" -#include "ui/views/corewm/shadow.h" -#include "ui/views/corewm/shadow_types.h" - -using std::make_pair; - -namespace views { -namespace corewm { - -namespace { - -ShadowType GetShadowTypeFromWindow(aura::Window* window) { - switch (window->type()) { - case aura::client::WINDOW_TYPE_NORMAL: - case aura::client::WINDOW_TYPE_PANEL: - case aura::client::WINDOW_TYPE_MENU: - case aura::client::WINDOW_TYPE_TOOLTIP: - return SHADOW_TYPE_RECTANGULAR; - default: - break; - } - return SHADOW_TYPE_NONE; -} - -bool ShouldUseSmallShadowForWindow(aura::Window* window) { - switch (window->type()) { - case aura::client::WINDOW_TYPE_MENU: - case aura::client::WINDOW_TYPE_TOOLTIP: - return true; - default: - break; - } - return false; -} - -// Returns the shadow style to be applied to |losing_active| when it is losing -// active to |gaining_active|. |gaining_active| may be of a type that hides when -// inactive, and as such we do not want to render |losing_active| as inactive. -Shadow::Style GetShadowStyleForWindowLosingActive( - aura::Window* losing_active, - aura::Window* gaining_active) { - if (gaining_active && aura::client::GetHideOnDeactivate(gaining_active)) { - aura::Window::Windows::const_iterator it = - std::find(losing_active->transient_children().begin(), - losing_active->transient_children().end(), - gaining_active); - if (it != losing_active->transient_children().end()) - return Shadow::STYLE_ACTIVE; - } - return Shadow::STYLE_INACTIVE; -} - -} // namespace - -// ShadowController::Impl ------------------------------------------------------ - -// Real implementation of the ShadowController. ShadowController observes -// ActivationChangeObserver, which are per ActivationClient, where as there is -// only a single Impl (as it observes all window creation by way of an -// EnvObserver). -class ShadowController::Impl : - public aura::EnvObserver, - public aura::WindowObserver, - public base::RefCounted<Impl> { - public: - // Returns the singleton instance, destroyed when there are no more refs. - static Impl* GetInstance(); - - // aura::EnvObserver override: - virtual void OnWindowInitialized(aura::Window* window) OVERRIDE; - - // aura::WindowObserver overrides: - virtual void OnWindowPropertyChanged( - aura::Window* window, const void* key, intptr_t old) OVERRIDE; - virtual void OnWindowBoundsChanged( - aura::Window* window, - const gfx::Rect& old_bounds, - const gfx::Rect& new_bounds) OVERRIDE; - virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE; - - private: - friend class base::RefCounted<Impl>; - friend class ShadowController; - friend class ShadowController::TestApi; - - typedef std::map<aura::Window*, linked_ptr<Shadow> > WindowShadowMap; - - Impl(); - virtual ~Impl(); - - // Forwarded from ShadowController. - void OnWindowActivated(aura::Window* gained_active, - aura::Window* lost_active); - - // Checks if |window| is visible and contains a property requesting a shadow. - bool ShouldShowShadowForWindow(aura::Window* window) const; - - // Returns |window|'s shadow from |window_shadows_|, or NULL if no shadow - // exists. - Shadow* GetShadowForWindow(aura::Window* window); - - // Updates the shadow styles for windows when activation changes. - void HandleWindowActivationChange(aura::Window* gaining_active, - aura::Window* losing_active); - - // Shows or hides |window|'s shadow as needed (creating the shadow if - // necessary). - void HandlePossibleShadowVisibilityChange(aura::Window* window); - - // Creates a new shadow for |window| and stores it in |window_shadows_|. The - // shadow's bounds are initialized and it is added to the window's layer. - void CreateShadowForWindow(aura::Window* window); - - WindowShadowMap window_shadows_; - - ScopedObserver<aura::Window, aura::WindowObserver> observer_manager_; - - static Impl* instance_; - - DISALLOW_COPY_AND_ASSIGN(Impl); -}; - -// static -ShadowController::Impl* ShadowController::Impl::instance_ = NULL; - -// static -ShadowController::Impl* ShadowController::Impl::GetInstance() { - if (!instance_) - instance_ = new Impl(); - return instance_; -} - -void ShadowController::Impl::OnWindowInitialized(aura::Window* window) { - observer_manager_.Add(window); - SetShadowType(window, GetShadowTypeFromWindow(window)); - HandlePossibleShadowVisibilityChange(window); -} - -void ShadowController::Impl::OnWindowPropertyChanged(aura::Window* window, - const void* key, - intptr_t old) { - if (key == kShadowTypeKey) { - HandlePossibleShadowVisibilityChange(window); - return; - } -} - -void ShadowController::Impl::OnWindowBoundsChanged( - aura::Window* window, - const gfx::Rect& old_bounds, - const gfx::Rect& new_bounds) { - Shadow* shadow = GetShadowForWindow(window); - if (shadow) - shadow->SetContentBounds(gfx::Rect(new_bounds.size())); -} - -void ShadowController::Impl::OnWindowDestroyed(aura::Window* window) { - window_shadows_.erase(window); - observer_manager_.Remove(window); -} - -void ShadowController::Impl::OnWindowActivated(aura::Window* gained_active, - aura::Window* lost_active) { - if (gained_active) { - Shadow* shadow = GetShadowForWindow(gained_active); - if (shadow && !ShouldUseSmallShadowForWindow(gained_active)) - shadow->SetStyle(Shadow::STYLE_ACTIVE); - } - if (lost_active) { - Shadow* shadow = GetShadowForWindow(lost_active); - if (shadow && !ShouldUseSmallShadowForWindow(lost_active)) { - shadow->SetStyle(GetShadowStyleForWindowLosingActive(lost_active, - gained_active)); - } - } -} - -bool ShadowController::Impl::ShouldShowShadowForWindow( - aura::Window* window) const { - const ShadowType type = GetShadowType(window); - switch (type) { - case SHADOW_TYPE_NONE: - return false; - case SHADOW_TYPE_RECTANGULAR: - return true; - default: - NOTREACHED() << "Unknown shadow type " << type; - return false; - } -} - -Shadow* ShadowController::Impl::GetShadowForWindow(aura::Window* window) { - WindowShadowMap::const_iterator it = window_shadows_.find(window); - return it != window_shadows_.end() ? it->second.get() : NULL; -} - -void ShadowController::Impl::HandlePossibleShadowVisibilityChange( - aura::Window* window) { - const bool should_show = ShouldShowShadowForWindow(window); - Shadow* shadow = GetShadowForWindow(window); - if (shadow) - shadow->layer()->SetVisible(should_show); - else if (should_show && !shadow) - CreateShadowForWindow(window); -} - -void ShadowController::Impl::CreateShadowForWindow(aura::Window* window) { - linked_ptr<Shadow> shadow(new Shadow()); - window_shadows_.insert(make_pair(window, shadow)); - - shadow->Init(ShouldUseSmallShadowForWindow(window) ? - Shadow::STYLE_SMALL : Shadow::STYLE_ACTIVE); - shadow->SetContentBounds(gfx::Rect(window->bounds().size())); - shadow->layer()->SetVisible(ShouldShowShadowForWindow(window)); - window->layer()->Add(shadow->layer()); -} - -ShadowController::Impl::Impl() - : observer_manager_(this) { - aura::Env::GetInstance()->AddObserver(this); -} - -ShadowController::Impl::~Impl() { - DCHECK_EQ(instance_, this); - aura::Env::GetInstance()->RemoveObserver(this); - instance_ = NULL; -} - -// ShadowController ------------------------------------------------------------ - -ShadowController::ShadowController( - aura::client::ActivationClient* activation_client) - : activation_client_(activation_client), - impl_(Impl::GetInstance()) { - // Watch for window activation changes. - activation_client_->AddObserver(this); -} - -ShadowController::~ShadowController() { - activation_client_->RemoveObserver(this); -} - -void ShadowController::OnWindowActivated(aura::Window* gained_active, - aura::Window* lost_active) { - impl_->OnWindowActivated(gained_active, lost_active); -} - -// ShadowController::TestApi --------------------------------------------------- - -Shadow* ShadowController::TestApi::GetShadowForWindow(aura::Window* window) { - return controller_->impl_->GetShadowForWindow(window); -} - -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/shadow_controller.h b/chromium/ui/views/corewm/shadow_controller.h deleted file mode 100644 index 2a7e29c88e5..00000000000 --- a/chromium/ui/views/corewm/shadow_controller.h +++ /dev/null @@ -1,71 +0,0 @@ -// 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_COREWM_SHADOW_CONTROLLER_H_ -#define UI_VIEWS_COREWM_SHADOW_CONTROLLER_H_ - -#include <map> - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/memory/ref_counted.h" -#include "ui/aura/client/activation_change_observer.h" -#include "ui/views/views_export.h" - -namespace aura { -class Window; -namespace client { -class ActivationClient; -} -} -namespace gfx { -class Rect; -} - -namespace views { -namespace corewm { - -class Shadow; - -// ShadowController observes changes to windows and creates and updates drop -// shadows as needed. ShadowController itself is light weight and per -// ActivationClient. ShadowController delegates to its implementation class, -// which observes all window creation. -class VIEWS_EXPORT ShadowController : - public aura::client::ActivationChangeObserver { - public: - class VIEWS_EXPORT TestApi { - public: - explicit TestApi(ShadowController* controller) : controller_(controller) {} - ~TestApi() {} - - Shadow* GetShadowForWindow(aura::Window* window); - - private: - ShadowController* controller_; // not owned - - DISALLOW_COPY_AND_ASSIGN(TestApi); - }; - - explicit ShadowController(aura::client::ActivationClient* activation_client); - virtual ~ShadowController(); - - // aura::client::ActivationChangeObserver overrides: - virtual void OnWindowActivated(aura::Window* gained_active, - aura::Window* lost_active) OVERRIDE; - - private: - class Impl; - - aura::client::ActivationClient* activation_client_; - - scoped_refptr<Impl> impl_; - - DISALLOW_COPY_AND_ASSIGN(ShadowController); -}; - -} // namespace corewm -} // namespace views - -#endif // UI_VIEWS_COREWM_SHADOW_CONTROLLER_H_ diff --git a/chromium/ui/views/corewm/shadow_controller_unittest.cc b/chromium/ui/views/corewm/shadow_controller_unittest.cc deleted file mode 100644 index 4140f7d83fb..00000000000 --- a/chromium/ui/views/corewm/shadow_controller_unittest.cc +++ /dev/null @@ -1,214 +0,0 @@ -// 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/corewm/shadow_controller.h" - -#include <algorithm> -#include <vector> - -#include "base/memory/scoped_ptr.h" -#include "ui/aura/client/activation_client.h" -#include "ui/aura/client/window_tree_client.h" -#include "ui/aura/root_window.h" -#include "ui/aura/test/aura_test_base.h" -#include "ui/aura/window.h" -#include "ui/compositor/layer.h" -#include "ui/views/corewm/shadow.h" -#include "ui/views/corewm/shadow_types.h" - -namespace views { -namespace corewm { - -class ShadowControllerTest : public aura::test::AuraTestBase { - public: - ShadowControllerTest() {} - virtual ~ShadowControllerTest() {} - - virtual void SetUp() OVERRIDE { - AuraTestBase::SetUp(); - aura::client::ActivationClient* activation_client = - aura::client::GetActivationClient(root_window()); - shadow_controller_.reset(new ShadowController(activation_client)); - } - virtual void TearDown() OVERRIDE { - shadow_controller_.reset(); - AuraTestBase::TearDown(); - } - - protected: - ShadowController* shadow_controller() { return shadow_controller_.get(); } - - void ActivateWindow(aura::Window* window) { - DCHECK(window); - DCHECK(window->GetRootWindow()); - aura::client::GetActivationClient(window->GetRootWindow())->ActivateWindow( - window); - } - - private: - scoped_ptr<ShadowController> shadow_controller_; - - DISALLOW_COPY_AND_ASSIGN(ShadowControllerTest); -}; - -// Tests that various methods in Window update the Shadow object as expected. -TEST_F(ShadowControllerTest, Shadow) { - scoped_ptr<aura::Window> window(new aura::Window(NULL)); - window->SetType(aura::client::WINDOW_TYPE_NORMAL); - window->Init(ui::LAYER_TEXTURED); - ParentWindow(window.get()); - - // We should create the shadow before the window is visible (the shadow's - // layer won't get drawn yet since it's a child of the window's layer). - ShadowController::TestApi api(shadow_controller()); - const Shadow* shadow = api.GetShadowForWindow(window.get()); - ASSERT_TRUE(shadow != NULL); - EXPECT_TRUE(shadow->layer()->visible()); - - // The shadow should remain visible after window visibility changes. - window->Show(); - EXPECT_TRUE(shadow->layer()->visible()); - window->Hide(); - EXPECT_TRUE(shadow->layer()->visible()); - - // If the shadow is disabled, it should be hidden. - SetShadowType(window.get(), SHADOW_TYPE_NONE); - window->Show(); - EXPECT_FALSE(shadow->layer()->visible()); - SetShadowType(window.get(), SHADOW_TYPE_RECTANGULAR); - EXPECT_TRUE(shadow->layer()->visible()); - - // The shadow's layer should be a child of the window's layer. - EXPECT_EQ(window->layer(), shadow->layer()->parent()); - - window->parent()->RemoveChild(window.get()); - aura::Window* window_ptr = window.get(); - window.reset(); - EXPECT_TRUE(api.GetShadowForWindow(window_ptr) == NULL); -} - -// Tests that the window's shadow's bounds are updated correctly. -TEST_F(ShadowControllerTest, ShadowBounds) { - scoped_ptr<aura::Window> window(new aura::Window(NULL)); - window->SetType(aura::client::WINDOW_TYPE_NORMAL); - window->Init(ui::LAYER_TEXTURED); - ParentWindow(window.get()); - window->Show(); - - const gfx::Rect kOldBounds(20, 30, 400, 300); - window->SetBounds(kOldBounds); - - // When the shadow is first created, it should use the window's size (but - // remain at the origin, since it's a child of the window's layer). - SetShadowType(window.get(), SHADOW_TYPE_RECTANGULAR); - ShadowController::TestApi api(shadow_controller()); - const Shadow* shadow = api.GetShadowForWindow(window.get()); - ASSERT_TRUE(shadow != NULL); - EXPECT_EQ(gfx::Rect(kOldBounds.size()).ToString(), - shadow->content_bounds().ToString()); - - // When we change the window's bounds, the shadow's should be updated too. - gfx::Rect kNewBounds(50, 60, 500, 400); - window->SetBounds(kNewBounds); - EXPECT_EQ(gfx::Rect(kNewBounds.size()).ToString(), - shadow->content_bounds().ToString()); -} - -// Tests that activating a window changes the shadow style. -TEST_F(ShadowControllerTest, ShadowStyle) { - ShadowController::TestApi api(shadow_controller()); - - scoped_ptr<aura::Window> window1(new aura::Window(NULL)); - window1->SetType(aura::client::WINDOW_TYPE_NORMAL); - window1->Init(ui::LAYER_TEXTURED); - ParentWindow(window1.get()); - window1->SetBounds(gfx::Rect(10, 20, 300, 400)); - window1->Show(); - ActivateWindow(window1.get()); - - // window1 is active, so style should have active appearance. - Shadow* shadow1 = api.GetShadowForWindow(window1.get()); - ASSERT_TRUE(shadow1 != NULL); - EXPECT_EQ(Shadow::STYLE_ACTIVE, shadow1->style()); - - // Create another window and activate it. - scoped_ptr<aura::Window> window2(new aura::Window(NULL)); - window2->SetType(aura::client::WINDOW_TYPE_NORMAL); - window2->Init(ui::LAYER_TEXTURED); - ParentWindow(window2.get()); - window2->SetBounds(gfx::Rect(11, 21, 301, 401)); - window2->Show(); - ActivateWindow(window2.get()); - - // window1 is now inactive, so shadow should go inactive. - Shadow* shadow2 = api.GetShadowForWindow(window2.get()); - ASSERT_TRUE(shadow2 != NULL); - EXPECT_EQ(Shadow::STYLE_INACTIVE, shadow1->style()); - EXPECT_EQ(Shadow::STYLE_ACTIVE, shadow2->style()); -} - -// Tests that we use smaller shadows for tooltips and menus. -TEST_F(ShadowControllerTest, SmallShadowsForTooltipsAndMenus) { - ShadowController::TestApi api(shadow_controller()); - - scoped_ptr<aura::Window> tooltip_window(new aura::Window(NULL)); - tooltip_window->SetType(aura::client::WINDOW_TYPE_TOOLTIP); - tooltip_window->Init(ui::LAYER_TEXTURED); - ParentWindow(tooltip_window.get()); - tooltip_window->SetBounds(gfx::Rect(10, 20, 300, 400)); - tooltip_window->Show(); - - Shadow* tooltip_shadow = api.GetShadowForWindow(tooltip_window.get()); - ASSERT_TRUE(tooltip_shadow != NULL); - EXPECT_EQ(Shadow::STYLE_SMALL, tooltip_shadow->style()); - - scoped_ptr<aura::Window> menu_window(new aura::Window(NULL)); - menu_window->SetType(aura::client::WINDOW_TYPE_MENU); - menu_window->Init(ui::LAYER_TEXTURED); - ParentWindow(menu_window.get()); - menu_window->SetBounds(gfx::Rect(10, 20, 300, 400)); - menu_window->Show(); - - Shadow* menu_shadow = api.GetShadowForWindow(tooltip_window.get()); - ASSERT_TRUE(menu_shadow != NULL); - EXPECT_EQ(Shadow::STYLE_SMALL, menu_shadow->style()); -} - -// http://crbug.com/120210 - transient parents of certain types of transients -// should not lose their shadow when they lose activation to the transient. -TEST_F(ShadowControllerTest, TransientParentKeepsActiveShadow) { - ShadowController::TestApi api(shadow_controller()); - - scoped_ptr<aura::Window> window1(new aura::Window(NULL)); - window1->SetType(aura::client::WINDOW_TYPE_NORMAL); - window1->Init(ui::LAYER_TEXTURED); - ParentWindow(window1.get()); - window1->SetBounds(gfx::Rect(10, 20, 300, 400)); - window1->Show(); - ActivateWindow(window1.get()); - - // window1 is active, so style should have active appearance. - Shadow* shadow1 = api.GetShadowForWindow(window1.get()); - ASSERT_TRUE(shadow1 != NULL); - EXPECT_EQ(Shadow::STYLE_ACTIVE, shadow1->style()); - - // Create a window that is transient to window1, and that has the 'hide on - // deactivate' property set. Upon activation, window1 should still have an - // active shadow. - scoped_ptr<aura::Window> window2(new aura::Window(NULL)); - window2->SetType(aura::client::WINDOW_TYPE_NORMAL); - window2->Init(ui::LAYER_TEXTURED); - ParentWindow(window2.get()); - window2->SetBounds(gfx::Rect(11, 21, 301, 401)); - window1->AddTransientChild(window2.get()); - aura::client::SetHideOnDeactivate(window2.get(), true); - window2->Show(); - ActivateWindow(window2.get()); - - // window1 is now inactive, but its shadow should still appear active. - EXPECT_EQ(Shadow::STYLE_ACTIVE, shadow1->style()); -} - -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/shadow_types.cc b/chromium/ui/views/corewm/shadow_types.cc deleted file mode 100644 index 1e3a937028f..00000000000 --- a/chromium/ui/views/corewm/shadow_types.cc +++ /dev/null @@ -1,25 +0,0 @@ -// 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/corewm/shadow_types.h" - -#include "ui/aura/window_property.h" - -DECLARE_WINDOW_PROPERTY_TYPE(views::corewm::ShadowType); - -namespace views { -namespace corewm { - -void SetShadowType(aura::Window* window, ShadowType shadow_type) { - window->SetProperty(kShadowTypeKey, shadow_type); -} - -ShadowType GetShadowType(aura::Window* window) { - return window->GetProperty(kShadowTypeKey); -} - -DEFINE_WINDOW_PROPERTY_KEY(ShadowType, kShadowTypeKey, SHADOW_TYPE_NONE); - -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/shadow_types.h b/chromium/ui/views/corewm/shadow_types.h deleted file mode 100644 index 046ad705a3a..00000000000 --- a/chromium/ui/views/corewm/shadow_types.h +++ /dev/null @@ -1,36 +0,0 @@ -// 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_COREWM_SHADOW_TYPES_H_ -#define UI_VIEWS_COREWM_SHADOW_TYPES_H_ - -#include "ui/aura/window.h" -#include "ui/views/views_export.h" - -namespace aura { -class Window; -} - -namespace views { -namespace corewm { - -// Different types of drop shadows that can be drawn under a window by the -// shell. Used as a value for the kShadowTypeKey property. -enum ShadowType { - // Starts at 0 due to the cast in GetShadowType(). - SHADOW_TYPE_NONE = 0, - SHADOW_TYPE_RECTANGULAR, -}; - -VIEWS_EXPORT void SetShadowType(aura::Window* window, ShadowType shadow_type); -VIEWS_EXPORT ShadowType GetShadowType(aura::Window* window); - -// A property key describing the drop shadow that should be displayed under the -// window. If unset, no shadow is displayed. -extern const aura::WindowProperty<ShadowType>* const kShadowTypeKey; - -} // namespace corewm -} // namespace views - -#endif // UI_VIEWS_COREWM_SHADOW_TYPES_H_ diff --git a/chromium/ui/views/corewm/tooltip.h b/chromium/ui/views/corewm/tooltip.h index f714ac9a57c..7fb15bc32ca 100644 --- a/chromium/ui/views/corewm/tooltip.h +++ b/chromium/ui/views/corewm/tooltip.h @@ -27,7 +27,7 @@ class VIEWS_EXPORT Tooltip { // Updates the text on the tooltip and resizes to fit. virtual void SetText(aura::Window* window, - const string16& tooltip_text, + const base::string16& tooltip_text, const gfx::Point& location) = 0; // Shows the tooltip at the specified location (in screen coordinates). diff --git a/chromium/ui/views/corewm/tooltip_aura.cc b/chromium/ui/views/corewm/tooltip_aura.cc index 7c11bd80bbc..f9f41b93722 100644 --- a/chromium/ui/views/corewm/tooltip_aura.cc +++ b/chromium/ui/views/corewm/tooltip_aura.cc @@ -4,23 +4,20 @@ #include "ui/views/corewm/tooltip_aura.h" -#include "base/command_line.h" #include "base/strings/string_split.h" -#include "ui/aura/root_window.h" #include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/screen.h" #include "ui/gfx/text_elider.h" +#include "ui/gfx/text_utils.h" +#include "ui/native_theme/native_theme.h" #include "ui/views/background.h" #include "ui/views/border.h" -#include "ui/views/corewm/corewm_switches.h" #include "ui/views/widget/widget.h" namespace { -const SkColor kTooltipBackground = 0xFFFFFFCC; -const SkColor kTooltipBorder = 0xFF646450; -const int kTooltipBorderWidth = 1; const int kTooltipHorizontalPadding = 3; // Max visual tooltip width. If a tooltip is greater than this width, it will @@ -53,13 +50,6 @@ views::Widget* CreateTooltipWidget(aura::Window* tooltip_window) { return widget; } -gfx::Font GetDefaultFont() { - // TODO(varunjain): implementation duplicated in tooltip_manager_aura. Figure - // out a way to merge. - return ui::ResourceBundle::GetSharedInstance().GetFont( - ui::ResourceBundle::BaseFont); -} - } // namespace namespace views { @@ -69,13 +59,6 @@ TooltipAura::TooltipAura(gfx::ScreenType screen_type) : screen_type_(screen_type), widget_(NULL), tooltip_window_(NULL) { - label_.set_background( - views::Background::CreateSolidBackground(kTooltipBackground)); - if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoDropShadows)) { - label_.set_border( - views::Border::CreateSolidBorder(kTooltipBorderWidth, - kTooltipBorder)); - } label_.set_owned_by_client(); label_.SetMultiLine(true); } @@ -85,8 +68,9 @@ TooltipAura::~TooltipAura() { } // static -void TooltipAura::TrimTooltipToFit(int max_width, - string16* text, +void TooltipAura::TrimTooltipToFit(const gfx::FontList& font_list, + int max_width, + base::string16* text, int* width, int* line_count) { *width = 0; @@ -95,27 +79,26 @@ void TooltipAura::TrimTooltipToFit(int max_width, // Determine the available width for the tooltip. int available_width = std::min(kTooltipMaxWidthPixels, max_width); - std::vector<string16> lines; + std::vector<base::string16> lines; base::SplitString(*text, '\n', &lines); - std::vector<string16> result_lines; + std::vector<base::string16> result_lines; // Format each line to fit. - gfx::Font font = GetDefaultFont(); - for (std::vector<string16>::iterator l = lines.begin(); l != lines.end(); - ++l) { + for (std::vector<base::string16>::iterator l = lines.begin(); + l != lines.end(); ++l) { // We break the line at word boundaries, then stuff as many words as we can // in the available width to the current line, and move the remaining words // to a new line. - std::vector<string16> words; + std::vector<base::string16> words; base::SplitStringDontTrim(*l, ' ', &words); int current_width = 0; - string16 line; - for (std::vector<string16>::iterator w = words.begin(); w != words.end(); - ++w) { - string16 word = *w; + base::string16 line; + for (std::vector<base::string16>::iterator w = words.begin(); + w != words.end(); ++w) { + base::string16 word = *w; if (w + 1 != words.end()) word.push_back(' '); - int word_width = font.GetStringWidth(word); + int word_width = gfx::GetStringWidth(word, font_list); if (current_width + word_width > available_width) { // Current width will exceed the available width. Must start a new line. if (!line.empty()) @@ -139,19 +122,19 @@ void TooltipAura::TrimTooltipToFit(int max_width, *line_count = result_lines.size(); // Flatten the result. - string16 result; - for (std::vector<string16>::iterator l = result_lines.begin(); + base::string16 result; + for (std::vector<base::string16>::iterator l = result_lines.begin(); l != result_lines.end(); ++l) { if (!result.empty()) result.push_back('\n'); - int line_width = font.GetStringWidth(*l); + int line_width = gfx::GetStringWidth(*l, font_list); // Since we only break at word boundaries, it could happen that due to some // very long word, line_width is greater than the available_width. In such // case, we simply truncate at available_width and add ellipses at the end. if (line_width > available_width) { *width = available_width; - result.append(gfx::ElideText(*l, font, available_width, - gfx::ELIDE_AT_END)); + result.append(gfx::ElideText(*l, font_list, available_width, + gfx::ELIDE_TAIL)); } else { *width = std::max(*width, line_width); result.append(*l); @@ -163,25 +146,9 @@ void TooltipAura::TrimTooltipToFit(int max_width, int TooltipAura::GetMaxWidth(const gfx::Point& location) const { // TODO(varunjain): implementation duplicated in tooltip_manager_aura. Figure // out a way to merge. - gfx::Rect display_bounds = GetBoundsForTooltip(location); - return (display_bounds.width() + 1) / 2; -} - -gfx::Rect TooltipAura::GetBoundsForTooltip( - const gfx::Point& origin) const { - DCHECK(tooltip_window_); - gfx::Rect widget_bounds; - // For Desktop aura we constrain the tooltip to the bounds of the Widget - // (which comes from the RootWindow). - if (screen_type_ == gfx::SCREEN_TYPE_NATIVE && - gfx::SCREEN_TYPE_NATIVE != gfx::SCREEN_TYPE_ALTERNATE) { - widget_bounds = tooltip_window_->GetDispatcher()->host()->GetBounds(); - } gfx::Screen* screen = gfx::Screen::GetScreenByType(screen_type_); - gfx::Rect bounds(screen->GetDisplayNearestPoint(origin).bounds()); - if (!widget_bounds.IsEmpty()) - bounds.Intersect(widget_bounds); - return bounds; + gfx::Rect display_bounds(screen->GetDisplayNearestPoint(location).bounds()); + return (display_bounds.width() + 1) / 2; } void TooltipAura::SetTooltipBounds(const gfx::Point& mouse_pos, @@ -191,7 +158,8 @@ void TooltipAura::SetTooltipBounds(const gfx::Point& mouse_pos, tooltip_height); tooltip_rect.Offset(kCursorOffsetX, kCursorOffsetY); - gfx::Rect display_bounds = GetBoundsForTooltip(mouse_pos); + gfx::Screen* screen = gfx::Screen::GetScreenByType(screen_type_); + gfx::Rect display_bounds(screen->GetDisplayNearestPoint(mouse_pos).bounds()); // If tooltip is out of bounds on the x axis, we simply shift it // horizontally by the offset. @@ -209,22 +177,6 @@ void TooltipAura::SetTooltipBounds(const gfx::Point& mouse_pos, widget_->SetBounds(tooltip_rect); } -void TooltipAura::CreateWidget() { - if (widget_) { - // If the window for which the tooltip is being displayed changes and if the - // tooltip window and the tooltip widget belong to different rootwindows - // then we need to recreate the tooltip widget under the active root window - // hierarchy to get it to display. - if (widget_->GetNativeWindow()->GetRootWindow() == - tooltip_window_->GetRootWindow()) - return; - DestroyWidget(); - } - widget_ = CreateTooltipWidget(tooltip_window_); - widget_->SetContentsView(&label_); - widget_->AddObserver(this); -} - void TooltipAura::DestroyWidget() { if (widget_) { widget_->RemoveObserver(this); @@ -234,29 +186,43 @@ void TooltipAura::DestroyWidget() { } void TooltipAura::SetText(aura::Window* window, - const string16& tooltip_text, + const base::string16& tooltip_text, const gfx::Point& location) { tooltip_window_ = window; int max_width, line_count; - string16 trimmed_text(tooltip_text); - TrimTooltipToFit( - GetMaxWidth(location), &trimmed_text, &max_width, &line_count); + base::string16 trimmed_text(tooltip_text); + TrimTooltipToFit(label_.font_list(), GetMaxWidth(location), &trimmed_text, + &max_width, &line_count); label_.SetText(trimmed_text); int width = max_width + 2 * kTooltipHorizontalPadding; int height = label_.GetHeightForWidth(max_width) + 2 * kTooltipVerticalPadding; - if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoDropShadows)) { - width += 2 * kTooltipBorderWidth; - height += 2 * kTooltipBorderWidth; + + if (!widget_) { + widget_ = CreateTooltipWidget(tooltip_window_); + widget_->SetContentsView(&label_); + widget_->AddObserver(this); } - CreateWidget(); + SetTooltipBounds(location, width, height); + + ui::NativeTheme* native_theme = widget_->GetNativeTheme(); + label_.set_background( + views::Background::CreateSolidBackground( + native_theme->GetSystemColor( + ui::NativeTheme::kColorId_TooltipBackground))); + + label_.SetAutoColorReadabilityEnabled(false); + label_.SetEnabledColor(native_theme->GetSystemColor( + ui::NativeTheme::kColorId_TooltipText)); } void TooltipAura::Show() { - if (widget_) + if (widget_) { widget_->Show(); + widget_->StackAtTop(); + } } void TooltipAura::Hide() { diff --git a/chromium/ui/views/corewm/tooltip_aura.h b/chromium/ui/views/corewm/tooltip_aura.h index 97f3737aa8e..ead119f5d95 100644 --- a/chromium/ui/views/corewm/tooltip_aura.h +++ b/chromium/ui/views/corewm/tooltip_aura.h @@ -10,6 +10,10 @@ #include "ui/views/corewm/tooltip.h" #include "ui/views/widget/widget_observer.h" +namespace gfx { +class FontList; +} // namespace gfx + namespace views { class Widget; @@ -24,11 +28,11 @@ class VIEWS_EXPORT TooltipAura : public Tooltip, public WidgetObserver { // Trims the tooltip to fit in the width |max_width|, setting |text| to the // clipped result, |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. |max_width| comes - // from GetMaxWidth(). - static void TrimTooltipToFit(int max_width, - string16* text, + // and |line_count| to the number of lines of text in the tooltip. |font_list| + // is used to layout |text|. |max_width| comes from GetMaxWidth(). + static void TrimTooltipToFit(const gfx::FontList& font_list, + int max_width, + base::string16* text, int* width, int* line_count); @@ -36,24 +40,18 @@ class VIEWS_EXPORT TooltipAura : public Tooltip, public WidgetObserver { // Returns the max width of the tooltip when shown at the specified location. int GetMaxWidth(const gfx::Point& location) const; - // Returns the bounds to fit the tooltip in. - gfx::Rect GetBoundsForTooltip(const gfx::Point& origin) const; - // Adjusts the bounds given by the arguments to fit inside the desktop // and applies the adjusted bounds to the label_. void SetTooltipBounds(const gfx::Point& mouse_pos, int tooltip_width, int tooltip_height); - // Makes sure |widget_| is valid, creating as necessary. - void CreateWidget(); - // Destroys |widget_|. void DestroyWidget(); // Tooltip: virtual void SetText(aura::Window* window, - const string16& tooltip_text, + const base::string16& tooltip_text, const gfx::Point& location) OVERRIDE; virtual void Show() OVERRIDE; virtual void Hide() OVERRIDE; diff --git a/chromium/ui/views/corewm/tooltip_aura_unittest.cc b/chromium/ui/views/corewm/tooltip_aura_unittest.cc index 75814abcad8..1e8981ad791 100644 --- a/chromium/ui/views/corewm/tooltip_aura_unittest.cc +++ b/chromium/ui/views/corewm/tooltip_aura_unittest.cc @@ -5,43 +5,44 @@ #include "ui/views/corewm/tooltip_aura.h" #include "base/strings/utf_string_conversions.h" +#include "ui/aura/test/aura_test_base.h" #include "ui/base/resource/resource_bundle.h" -#include "ui/gfx/font.h" +#include "ui/gfx/font_list.h" #include "ui/gfx/text_elider.h" -#include "ui/views/test/views_test_base.h" +#include "ui/gfx/text_utils.h" + +using base::ASCIIToUTF16; +using base::UTF8ToUTF16; namespace views { namespace corewm { -typedef ViewsTestBase TooltipAuraTest; - -// TODO(sky): clean this up. -gfx::Font GetDefaultFont() { - return ui::ResourceBundle::GetSharedInstance().GetFont( - ui::ResourceBundle::BaseFont); -} +typedef aura::test::AuraTestBase TooltipAuraTest; TEST_F(TooltipAuraTest, TrimTooltipToFitTests) { + const gfx::FontList font_list; const int max_width = 4000; - string16 tooltip; + base::string16 tooltip; int width, line_count, expect_lines; int max_pixel_width = 400; // copied from constants in tooltip_controller.cc int max_lines = 10; // copied from constants in tooltip_controller.cc - gfx::Font font = GetDefaultFont(); size_t tooltip_len; // Error in computed size vs. expected size should not be greater than the // size of the longest word. - int error_in_pixel_width = font.GetStringWidth(ASCIIToUTF16("tooltip")); + int error_in_pixel_width = gfx::GetStringWidth(ASCIIToUTF16("tooltip"), + font_list); // Long tooltips should wrap to next line tooltip.clear(); width = line_count = -1; expect_lines = 3; - for (; font.GetStringWidth(tooltip) <= (expect_lines - 1) * max_pixel_width;) + for (; gfx::GetStringWidth(tooltip, font_list) <= + (expect_lines - 1) * max_pixel_width;) tooltip.append(ASCIIToUTF16("This is part of the tooltip")); tooltip_len = tooltip.length(); - TooltipAura::TrimTooltipToFit(max_width, &tooltip, &width, &line_count); + TooltipAura::TrimTooltipToFit(font_list, max_width, &tooltip, &width, + &line_count); EXPECT_NEAR(max_pixel_width, width, error_in_pixel_width); EXPECT_EQ(expect_lines, line_count); EXPECT_EQ(tooltip_len + expect_lines - 1, tooltip.length()); @@ -50,9 +51,11 @@ TEST_F(TooltipAuraTest, TrimTooltipToFitTests) { tooltip.clear(); width = line_count = -1; expect_lines = 13; - for (; font.GetStringWidth(tooltip) <= (expect_lines - 1) * max_pixel_width;) + for (; gfx::GetStringWidth(tooltip, font_list) <= + (expect_lines - 1) * max_pixel_width;) tooltip.append(ASCIIToUTF16("This is part of the tooltip")); - TooltipAura::TrimTooltipToFit(max_width, &tooltip, &width, &line_count); + TooltipAura::TrimTooltipToFit(font_list, max_width, &tooltip, &width, + &line_count); EXPECT_NEAR(max_pixel_width, width, error_in_pixel_width); EXPECT_EQ(max_lines, line_count); @@ -60,11 +63,13 @@ TEST_F(TooltipAuraTest, TrimTooltipToFitTests) { tooltip.clear(); width = line_count = -1; expect_lines = 4; - for (; font.GetStringWidth(tooltip) <= (expect_lines - 2) * max_pixel_width;) + for (; gfx::GetStringWidth(tooltip, font_list) <= + (expect_lines - 2) * max_pixel_width;) tooltip.append(ASCIIToUTF16("This is part of the tooltip")); tooltip.insert(tooltip.length() / 2, ASCIIToUTF16("\n")); tooltip_len = tooltip.length(); - TooltipAura::TrimTooltipToFit(max_width, &tooltip, &width, &line_count); + TooltipAura::TrimTooltipToFit(font_list, max_width, &tooltip, &width, + &line_count); EXPECT_NEAR(max_pixel_width, width, error_in_pixel_width); EXPECT_EQ(expect_lines, line_count); // We may have inserted the line break above near a space which will get @@ -76,19 +81,23 @@ TEST_F(TooltipAuraTest, TrimTooltipToFitTests) { tooltip.clear(); width = line_count = -1; tooltip = UTF8ToUTF16(std::string('a', max_pixel_width)); - TooltipAura::TrimTooltipToFit(max_width, &tooltip, &width, &line_count); + TooltipAura::TrimTooltipToFit(font_list, max_width, &tooltip, &width, + &line_count); EXPECT_NEAR(max_pixel_width, width, 5); EXPECT_EQ(1, line_count); - EXPECT_EQ(gfx::ElideText(UTF8ToUTF16(std::string('a', max_pixel_width)), font, - max_pixel_width, gfx::ELIDE_AT_END), tooltip); + EXPECT_EQ(gfx::ElideText(UTF8ToUTF16(std::string('a', max_pixel_width)), + font_list, max_pixel_width, gfx::ELIDE_TAIL), + tooltip); #endif // Normal small tooltip should stay as is. tooltip.clear(); width = line_count = -1; tooltip = ASCIIToUTF16("Small Tooltip"); - TooltipAura::TrimTooltipToFit(max_width, &tooltip, &width, &line_count); - EXPECT_EQ(font.GetStringWidth(ASCIIToUTF16("Small Tooltip")), width); + TooltipAura::TrimTooltipToFit(font_list, max_width, &tooltip, &width, + &line_count); + EXPECT_EQ(gfx::GetStringWidth(ASCIIToUTF16("Small Tooltip"), font_list), + width); EXPECT_EQ(1, line_count); EXPECT_EQ(ASCIIToUTF16("Small Tooltip"), tooltip); @@ -96,10 +105,13 @@ TEST_F(TooltipAuraTest, TrimTooltipToFitTests) { tooltip.clear(); width = line_count = -1; tooltip = ASCIIToUTF16("Multi line\nTooltip"); - TooltipAura::TrimTooltipToFit(max_width, &tooltip, &width, &line_count); - int expected_width = font.GetStringWidth(ASCIIToUTF16("Multi line")); + TooltipAura::TrimTooltipToFit(font_list, max_width, &tooltip, &width, + &line_count); + int expected_width = gfx::GetStringWidth(ASCIIToUTF16("Multi line"), + font_list); expected_width = std::max(expected_width, - font.GetStringWidth(ASCIIToUTF16("Tooltip"))); + gfx::GetStringWidth(ASCIIToUTF16("Tooltip"), + font_list)); EXPECT_EQ(expected_width, width); EXPECT_EQ(2, line_count); EXPECT_EQ(ASCIIToUTF16("Multi line\nTooltip"), tooltip); @@ -108,8 +120,10 @@ TEST_F(TooltipAuraTest, TrimTooltipToFitTests) { tooltip.clear(); width = line_count = -1; tooltip = ASCIIToUTF16("Small Tool t\tip"); - TooltipAura::TrimTooltipToFit(max_width, &tooltip, &width, &line_count); - EXPECT_EQ(font.GetStringWidth(ASCIIToUTF16("Small Tool t\tip")), width); + TooltipAura::TrimTooltipToFit(font_list, max_width, &tooltip, &width, + &line_count); + EXPECT_EQ(gfx::GetStringWidth(ASCIIToUTF16("Small Tool t\tip"), font_list), + width); EXPECT_EQ(1, line_count); EXPECT_EQ(ASCIIToUTF16("Small Tool t\tip"), tooltip); } diff --git a/chromium/ui/views/corewm/tooltip_controller.cc b/chromium/ui/views/corewm/tooltip_controller.cc index b411688397d..b1ffc1c7de8 100644 --- a/chromium/ui/views/corewm/tooltip_controller.cc +++ b/chromium/ui/views/corewm/tooltip_controller.cc @@ -10,7 +10,6 @@ #include "base/time/time.h" #include "ui/aura/client/capture_client.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/env.h" #include "ui/aura/window.h" @@ -20,6 +19,7 @@ #include "ui/gfx/screen.h" #include "ui/views/corewm/tooltip.h" #include "ui/views/widget/tooltip_manager.h" +#include "ui/wm/public/drag_drop_client.h" namespace views { namespace corewm { @@ -115,6 +115,7 @@ aura::Window* GetTooltipTarget(const ui::MouseEvent& event, TooltipController::TooltipController(scoped_ptr<Tooltip> tooltip) : tooltip_window_(NULL), + tooltip_id_(NULL), tooltip_window_at_mouse_press_(NULL), tooltip_(tooltip.Pass()), tooltips_enabled_(true) { @@ -133,6 +134,18 @@ void TooltipController::UpdateTooltip(aura::Window* target) { if (tooltip_window_ == target && tooltip_->IsVisible()) UpdateIfRequired(); + // Reset |tooltip_window_at_mouse_press_| if the moving within the same window + // but over a region that has different tooltip text. By resetting + // |tooltip_window_at_mouse_press_| we ensure the next time the timer fires + // we'll requery for the tooltip text. + // This handles the case of clicking on a view, moving within the same window + // but over a different view, than back to the original. + if (tooltip_window_at_mouse_press_ && + target == tooltip_window_at_mouse_press_ && + aura::client::GetTooltipText(target) != tooltip_text_at_mouse_press_) { + tooltip_window_at_mouse_press_ = NULL; + } + // If we had stopped the tooltip timer for some reason, we must restart it if // there is a change in the tooltip. if (!tooltip_timer_.IsRunning()) { @@ -174,14 +187,15 @@ void TooltipController::OnMouseEvent(ui::MouseEvent* event) { case ui::ET_MOUSE_MOVED: case ui::ET_MOUSE_DRAGGED: { curr_mouse_loc_ = event->location(); - aura::Window* target = GetTooltipTarget(*event, &curr_mouse_loc_); - if (tooltip_window_ != target) { - if (tooltip_window_) - tooltip_window_->RemoveObserver(this); - tooltip_window_ = target; - if (tooltip_window_) - tooltip_window_->AddObserver(this); + aura::Window* target = NULL; + // Avoid a call to gfx::Screen::GetWindowAtScreenPoint() since it can be + // very expensive on X11 in cases when the tooltip is hidden anyway. + if (tooltips_enabled_ && + !aura::Env::GetInstance()->IsMouseButtonDown() && + !IsDragDropInProgress()) { + target = GetTooltipTarget(*event, &curr_mouse_loc_); } + SetTooltipWindow(target); if (tooltip_timer_.IsRunning()) tooltip_timer_.Reset(); @@ -214,13 +228,12 @@ void TooltipController::OnTouchEvent(ui::TouchEvent* event) { // touch events. // Hide the tooltip for touch events. tooltip_->Hide(); - if (tooltip_window_) - tooltip_window_->RemoveObserver(this); - tooltip_window_ = NULL; + SetTooltipWindow(NULL); } void TooltipController::OnCancelMode(ui::CancelModeEvent* event) { tooltip_->Hide(); + SetTooltipWindow(NULL); } void TooltipController::OnWindowDestroyed(aura::Window* window) { @@ -255,7 +268,7 @@ void TooltipController::UpdateIfRequired() { return; } - string16 tooltip_text; + base::string16 tooltip_text; if (tooltip_window_) tooltip_text = aura::client::GetTooltipText(tooltip_window_); @@ -270,12 +283,19 @@ void TooltipController::UpdateIfRequired() { tooltip_window_at_mouse_press_ = NULL; } + // If the uniqueness indicator is different from the previously encountered + // one, we should force tooltip update + const void* tooltip_id = aura::client::GetTooltipId(tooltip_window_); + bool ids_differ = false; + ids_differ = tooltip_id_ != tooltip_id; + tooltip_id_ = tooltip_id; + // We add the !tooltip_->IsVisible() below because when we come here from // TooltipTimerFired(), the tooltip_text may not have changed but we still // want to update the tooltip because the timer has fired. // If we come here from UpdateTooltip(), we have already checked for tooltip // visibility and this check below will have no effect. - if (tooltip_text_ != tooltip_text || !tooltip_->IsVisible()) { + if (tooltip_text_ != tooltip_text || !tooltip_->IsVisible() || ids_differ) { tooltip_shown_timer_.Stop(); tooltip_text_ = tooltip_text; base::string16 trimmed_text(tooltip_text_); @@ -283,13 +303,14 @@ void TooltipController::UpdateIfRequired() { // If the string consists entirely of whitespace, then don't both showing it // (an empty tooltip is useless). base::string16 whitespace_removed_text; - TrimWhitespace(trimmed_text, TRIM_ALL, &whitespace_removed_text); + base::TrimWhitespace(trimmed_text, base::TRIM_ALL, + &whitespace_removed_text); if (whitespace_removed_text.empty()) { tooltip_->Hide(); } else { gfx::Point widget_loc = curr_mouse_loc_ + tooltip_window_->GetBoundsInScreen().OffsetFromOrigin(); - tooltip_->SetText(tooltip_window_, trimmed_text, widget_loc); + tooltip_->SetText(tooltip_window_, whitespace_removed_text, widget_loc); tooltip_->Show(); int timeout = GetTooltipShownTimeout(); if (timeout > 0) { @@ -333,5 +354,15 @@ int TooltipController::GetTooltipShownTimeout() { return it->second; } +void TooltipController::SetTooltipWindow(aura::Window* target) { + if (tooltip_window_ == target) + return; + if (tooltip_window_) + tooltip_window_->RemoveObserver(this); + tooltip_window_ = target; + if (tooltip_window_) + tooltip_window_->AddObserver(this); +} + } // namespace corewm } // namespace views diff --git a/chromium/ui/views/corewm/tooltip_controller.h b/chromium/ui/views/corewm/tooltip_controller.h index 66b6a3224c7..e0453341f00 100644 --- a/chromium/ui/views/corewm/tooltip_controller.h +++ b/chromium/ui/views/corewm/tooltip_controller.h @@ -10,11 +10,11 @@ #include "base/memory/scoped_ptr.h" #include "base/strings/string16.h" #include "base/timer/timer.h" -#include "ui/aura/client/tooltip_client.h" #include "ui/aura/window_observer.h" #include "ui/events/event_handler.h" #include "ui/gfx/point.h" #include "ui/views/views_export.h" +#include "ui/wm/public/tooltip_client.h" namespace aura { class Window; @@ -61,7 +61,7 @@ class VIEWS_EXPORT TooltipController : public aura::client::TooltipClient, void TooltipShownTimerFired(); // Updates the tooltip if required (if there is any change in the tooltip - // text or the aura::Window. + // text, tooltip id or the aura::Window). void UpdateIfRequired(); // Only used in tests. @@ -74,8 +74,14 @@ class VIEWS_EXPORT TooltipController : public aura::client::TooltipClient, int GetTooltipShownTimeout(); + // Sets tooltip window to |target| if it is different from existing window. + // Calls RemoveObserver on the existing window if it is not NULL. + // Calls AddObserver on the new window if it is not NULL. + void SetTooltipWindow(aura::Window* target); + aura::Window* tooltip_window_; base::string16 tooltip_text_; + const void* tooltip_id_; // These fields are for tracking state when the user presses a mouse button. aura::Window* tooltip_window_at_mouse_press_; diff --git a/chromium/ui/views/corewm/tooltip_controller_test_helper.cc b/chromium/ui/views/corewm/tooltip_controller_test_helper.cc index 4794a6e73e4..f96b08dc50a 100644 --- a/chromium/ui/views/corewm/tooltip_controller_test_helper.cc +++ b/chromium/ui/views/corewm/tooltip_controller_test_helper.cc @@ -19,7 +19,7 @@ TooltipControllerTestHelper::TooltipControllerTestHelper( TooltipControllerTestHelper::~TooltipControllerTestHelper() { } -string16 TooltipControllerTestHelper::GetTooltipText() { +base::string16 TooltipControllerTestHelper::GetTooltipText() { return controller_->tooltip_text_; } @@ -55,7 +55,7 @@ TooltipTestView::~TooltipTestView() { } bool TooltipTestView::GetTooltipText(const gfx::Point& p, - string16* tooltip) const { + base::string16* tooltip) const { *tooltip = tooltip_text_; return true; } diff --git a/chromium/ui/views/corewm/tooltip_controller_test_helper.h b/chromium/ui/views/corewm/tooltip_controller_test_helper.h index 58386200000..4451ff35fec 100644 --- a/chromium/ui/views/corewm/tooltip_controller_test_helper.h +++ b/chromium/ui/views/corewm/tooltip_controller_test_helper.h @@ -31,7 +31,7 @@ class TooltipControllerTestHelper { TooltipController* controller() { return controller_; } // These are mostly cover methods for TooltipController private methods. - string16 GetTooltipText(); + base::string16 GetTooltipText(); aura::Window* GetTooltipWindow(); void FireTooltipTimer(); bool IsTooltipTimerRunning(); @@ -51,14 +51,16 @@ class TooltipTestView : public views::View { TooltipTestView(); virtual ~TooltipTestView(); - void set_tooltip_text(string16 tooltip_text) { tooltip_text_ = tooltip_text; } + void set_tooltip_text(base::string16 tooltip_text) { + tooltip_text_ = tooltip_text; + } // Overridden from views::View virtual bool GetTooltipText(const gfx::Point& p, - string16* tooltip) const OVERRIDE; + base::string16* tooltip) const OVERRIDE; private: - string16 tooltip_text_; + base::string16 tooltip_text_; DISALLOW_COPY_AND_ASSIGN(TooltipTestView); }; diff --git a/chromium/ui/views/corewm/tooltip_controller_unittest.cc b/chromium/ui/views/corewm/tooltip_controller_unittest.cc index 6ebf4b9f828..e3487ae854b 100644 --- a/chromium/ui/views/corewm/tooltip_controller_unittest.cc +++ b/chromium/ui/views/corewm/tooltip_controller_unittest.cc @@ -7,14 +7,13 @@ #include "base/strings/utf_string_conversions.h" #include "ui/aura/client/cursor_client.h" #include "ui/aura/client/screen_position_client.h" -#include "ui/aura/client/tooltip_client.h" -#include "ui/aura/client/window_types.h" #include "ui/aura/env.h" -#include "ui/aura/root_window.h" #include "ui/aura/test/aura_test_base.h" #include "ui/aura/test/event_generator.h" #include "ui/aura/test/test_screen.h" +#include "ui/aura/test/test_window_delegate.h" #include "ui/aura/window.h" +#include "ui/aura/window_event_dispatcher.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/font.h" #include "ui/gfx/point.h" @@ -23,9 +22,15 @@ #include "ui/gfx/text_elider.h" #include "ui/views/corewm/tooltip_aura.h" #include "ui/views/corewm/tooltip_controller_test_helper.h" +#include "ui/views/test/desktop_test_views_delegate.h" +#include "ui/views/test/test_views_delegate.h" #include "ui/views/view.h" #include "ui/views/widget/tooltip_manager.h" #include "ui/views/widget/widget.h" +#include "ui/wm/core/default_activation_client.h" +#include "ui/wm/core/wm_state.h" +#include "ui/wm/public/tooltip_client.h" +#include "ui/wm/public/window_types.h" #if defined(OS_WIN) #include "ui/base/win/scoped_ole_initializer.h" @@ -36,6 +41,8 @@ #include "ui/views/widget/desktop_aura/desktop_screen.h" #endif +using base::ASCIIToUTF16; + namespace views { namespace corewm { namespace test { @@ -72,7 +79,14 @@ class TooltipControllerTest : public aura::test::AuraTestBase { virtual ~TooltipControllerTest() {} virtual void SetUp() OVERRIDE { +#if defined(OS_CHROMEOS) + views_delegate_.reset(new TestViewsDelegate); +#else + views_delegate_.reset(new DesktopTestViewsDelegate); +#endif + aura::test::AuraTestBase::SetUp(); + new wm::DefaultActivationClient(root_window()); #if defined(OS_CHROMEOS) controller_.reset(new TooltipController( scoped_ptr<views::corewm::Tooltip>( @@ -100,6 +114,7 @@ class TooltipControllerTest : public aura::test::AuraTestBase { helper_.reset(); widget_.reset(); aura::test::AuraTestBase::TearDown(); + views_delegate_.reset(); } protected: @@ -126,6 +141,9 @@ class TooltipControllerTest : public aura::test::AuraTestBase { private: scoped_ptr<TooltipController> controller_; + + scoped_ptr<views::TestViewsDelegate> views_delegate_; + #if defined(OS_WIN) ui::ScopedOleInitializer ole_initializer_; #endif @@ -135,15 +153,15 @@ class TooltipControllerTest : public aura::test::AuraTestBase { TEST_F(TooltipControllerTest, ViewTooltip) { view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text")); - EXPECT_EQ(string16(), helper_->GetTooltipText()); + EXPECT_EQ(base::string16(), helper_->GetTooltipText()); EXPECT_EQ(NULL, helper_->GetTooltipWindow()); generator_->MoveMouseToCenterOf(GetWindow()); EXPECT_EQ(GetWindow(), GetRootWindow()->GetEventHandlerForPoint( generator_->current_location())); - string16 expected_tooltip = ASCIIToUTF16("Tooltip Text"); + base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text"); EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(GetWindow())); - EXPECT_EQ(string16(), helper_->GetTooltipText()); + EXPECT_EQ(base::string16(), helper_->GetTooltipText()); EXPECT_EQ(GetWindow(), helper_->GetTooltipWindow()); // Fire tooltip timer so tooltip becomes visible. @@ -160,7 +178,7 @@ TEST_F(TooltipControllerTest, ViewTooltip) { TEST_F(TooltipControllerTest, TooltipsInMultipleViews) { view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text")); - EXPECT_EQ(string16(), helper_->GetTooltipText()); + EXPECT_EQ(base::string16(), helper_->GetTooltipText()); EXPECT_EQ(NULL, helper_->GetTooltipWindow()); PrepareSecondView(); @@ -176,7 +194,7 @@ TEST_F(TooltipControllerTest, TooltipsInMultipleViews) { EXPECT_TRUE(helper_->IsTooltipVisible()); EXPECT_EQ(window, root_window->GetEventHandlerForPoint( generator_->current_location())); - string16 expected_tooltip = ASCIIToUTF16("Tooltip Text"); + base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text"); EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window)); EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); EXPECT_EQ(window, helper_->GetTooltipWindow()); @@ -186,7 +204,7 @@ TEST_F(TooltipControllerTest, TooltipsInMultipleViews) { EXPECT_FALSE(helper_->IsTooltipVisible()); EXPECT_EQ(window, root_window->GetEventHandlerForPoint( generator_->current_location())); - string16 expected_tooltip; // = "" + base::string16 expected_tooltip; // = "" EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window)); EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); EXPECT_EQ(window, helper_->GetTooltipWindow()); @@ -195,11 +213,11 @@ TEST_F(TooltipControllerTest, TooltipsInMultipleViews) { TEST_F(TooltipControllerTest, EnableOrDisableTooltips) { view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text")); - EXPECT_EQ(string16(), helper_->GetTooltipText()); + EXPECT_EQ(base::string16(), helper_->GetTooltipText()); EXPECT_EQ(NULL, helper_->GetTooltipWindow()); generator_->MoveMouseRelativeTo(GetWindow(), view_->bounds().CenterPoint()); - string16 expected_tooltip = ASCIIToUTF16("Tooltip Text"); + base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text"); // Fire tooltip timer so tooltip becomes visible. helper_->FireTooltipTimer(); @@ -221,7 +239,7 @@ TEST_F(TooltipControllerTest, EnableOrDisableTooltips) { // Verifies tooltip isn't shown if tooltip text consists entirely of whitespace. TEST_F(TooltipControllerTest, DontShowEmptyTooltips) { view_->set_tooltip_text(ASCIIToUTF16(" ")); - EXPECT_EQ(string16(), helper_->GetTooltipText()); + EXPECT_EQ(base::string16(), helper_->GetTooltipText()); EXPECT_EQ(NULL, helper_->GetTooltipWindow()); generator_->MoveMouseRelativeTo(GetWindow(), view_->bounds().CenterPoint()); @@ -232,7 +250,7 @@ TEST_F(TooltipControllerTest, DontShowEmptyTooltips) { TEST_F(TooltipControllerTest, TooltipHidesOnKeyPressAndStaysHiddenUntilChange) { view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 1")); - EXPECT_EQ(string16(), helper_->GetTooltipText()); + EXPECT_EQ(base::string16(), helper_->GetTooltipText()); EXPECT_EQ(NULL, helper_->GetTooltipWindow()); TooltipTestView* view2 = PrepareSecondView(); @@ -261,7 +279,7 @@ TEST_F(TooltipControllerTest, TooltipHidesOnKeyPressAndStaysHiddenUntilChange) { EXPECT_EQ(window, GetRootWindow()->GetEventHandlerForPoint( generator_->current_location())); - string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 1"); + base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 1"); EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window)); EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); EXPECT_EQ(window, helper_->GetTooltipWindow()); @@ -273,7 +291,7 @@ TEST_F(TooltipControllerTest, TooltipHidesOnKeyPressAndStaysHiddenUntilChange) { helper_->FireTooltipTimer(); EXPECT_TRUE(helper_->IsTooltipVisible()); EXPECT_TRUE(helper_->IsTooltipShownTimerRunning()); - string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 2"); + base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 2"); EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window)); EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); EXPECT_EQ(window, helper_->GetTooltipWindow()); @@ -281,7 +299,7 @@ TEST_F(TooltipControllerTest, TooltipHidesOnKeyPressAndStaysHiddenUntilChange) { TEST_F(TooltipControllerTest, TooltipHidesOnTimeoutAndStaysHiddenUntilChange) { view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text for view 1")); - EXPECT_EQ(string16(), helper_->GetTooltipText()); + EXPECT_EQ(base::string16(), helper_->GetTooltipText()); EXPECT_EQ(NULL, helper_->GetTooltipWindow()); TooltipTestView* view2 = PrepareSecondView(); @@ -309,7 +327,7 @@ TEST_F(TooltipControllerTest, TooltipHidesOnTimeoutAndStaysHiddenUntilChange) { EXPECT_FALSE(helper_->IsTooltipShownTimerRunning()); EXPECT_EQ(window, GetRootWindow()->GetEventHandlerForPoint( generator_->current_location())); - string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 1"); + base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 1"); EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window)); EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); EXPECT_EQ(window, helper_->GetTooltipWindow()); @@ -321,7 +339,7 @@ TEST_F(TooltipControllerTest, TooltipHidesOnTimeoutAndStaysHiddenUntilChange) { helper_->FireTooltipTimer(); EXPECT_TRUE(helper_->IsTooltipVisible()); EXPECT_TRUE(helper_->IsTooltipShownTimerRunning()); - string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 2"); + base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text for view 2"); EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window)); EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); EXPECT_EQ(window, helper_->GetTooltipWindow()); @@ -331,9 +349,9 @@ TEST_F(TooltipControllerTest, TooltipHidesOnTimeoutAndStaysHiddenUntilChange) { TEST_F(TooltipControllerTest, HideOnExit) { view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text")); generator_->MoveMouseToCenterOf(GetWindow()); - string16 expected_tooltip = ASCIIToUTF16("Tooltip Text"); + base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text"); EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(GetWindow())); - EXPECT_EQ(string16(), helper_->GetTooltipText()); + EXPECT_EQ(base::string16(), helper_->GetTooltipText()); EXPECT_EQ(GetWindow(), helper_->GetTooltipWindow()); // Fire tooltip timer so tooltip becomes visible. @@ -344,6 +362,44 @@ TEST_F(TooltipControllerTest, HideOnExit) { EXPECT_FALSE(helper_->IsTooltipVisible()); } +TEST_F(TooltipControllerTest, ReshowOnClickAfterEnterExit) { + // Owned by |view_|. + TooltipTestView* v1 = new TooltipTestView; + TooltipTestView* v2 = new TooltipTestView; + view_->AddChildView(v1); + view_->AddChildView(v2); + gfx::Rect view_bounds(view_->GetLocalBounds()); + view_bounds.set_height(view_bounds.height() / 2); + v1->SetBoundsRect(view_bounds); + view_bounds.set_y(view_bounds.height()); + v2->SetBoundsRect(view_bounds); + const base::string16 v1_tt(ASCIIToUTF16("v1")); + const base::string16 v2_tt(ASCIIToUTF16("v2")); + v1->set_tooltip_text(v1_tt); + v2->set_tooltip_text(v2_tt); + + gfx::Point v1_point(1, 1); + View::ConvertPointToWidget(v1, &v1_point); + generator_->MoveMouseRelativeTo(GetWindow(), v1_point); + + // Fire tooltip timer so tooltip becomes visible. + helper_->FireTooltipTimer(); + EXPECT_TRUE(helper_->IsTooltipVisible()); + EXPECT_EQ(v1_tt, helper_->GetTooltipText()); + + // Press the mouse, move to v2 and back to v1. + generator_->ClickLeftButton(); + + gfx::Point v2_point(1, 1); + View::ConvertPointToWidget(v2, &v2_point); + generator_->MoveMouseRelativeTo(GetWindow(), v2_point); + generator_->MoveMouseRelativeTo(GetWindow(), v1_point); + + helper_->FireTooltipTimer(); + EXPECT_TRUE(helper_->IsTooltipVisible()); + EXPECT_EQ(v1_tt, helper_->GetTooltipText()); +} + namespace { // Returns the index of |window| in its parent's children. @@ -419,11 +475,12 @@ class TooltipControllerCaptureTest : public TooltipControllerTest { // Verifies when capture is released the TooltipController resets state. TEST_F(TooltipControllerCaptureTest, CloseOnCaptureLost) { view_->GetWidget()->SetCapture(view_); + RunAllPendingInMessageLoop(); view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text")); generator_->MoveMouseToCenterOf(GetWindow()); - string16 expected_tooltip = ASCIIToUTF16("Tooltip Text"); + base::string16 expected_tooltip = ASCIIToUTF16("Tooltip Text"); EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(GetWindow())); - EXPECT_EQ(string16(), helper_->GetTooltipText()); + EXPECT_EQ(base::string16(), helper_->GetTooltipText()); EXPECT_EQ(GetWindow(), helper_->GetTooltipWindow()); // Fire tooltip timer so tooltip becomes visible. @@ -444,8 +501,8 @@ TEST_F(TooltipControllerCaptureTest, CloseOnCaptureLost) { #endif // Verifies the correct window is found for tooltips when there is a capture. TEST_F(TooltipControllerCaptureTest, MAYBE_Capture) { - const string16 tooltip_text(ASCIIToUTF16("1")); - const string16 tooltip_text2(ASCIIToUTF16("2")); + const base::string16 tooltip_text(ASCIIToUTF16("1")); + const base::string16 tooltip_text2(ASCIIToUTF16("2")); widget_->SetBounds(gfx::Rect(0, 0, 200, 200)); view_->set_tooltip_text(tooltip_text); @@ -490,120 +547,285 @@ TEST_F(TooltipControllerCaptureTest, MAYBE_Capture) { widget2.reset(); } -#if !defined(OS_CHROMEOS) -// This test creates two top level windows and verifies that the tooltip -// displays correctly when mouse moves are dispatched to these windows. -// Additionally it also verifies that the tooltip is reparented to the new -// window when mouse moves are dispatched to it. -TEST_F(TooltipControllerTest, TooltipsInMultipleRootWindows) { - view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text For RootWindow1")); - EXPECT_EQ(string16(), helper_->GetTooltipText()); - EXPECT_EQ(NULL, helper_->GetTooltipWindow()); +namespace { - aura::Window* window = GetWindow(); - aura::Window* root_window = GetRootWindow(); +class TestTooltip : public Tooltip { + public: + TestTooltip() : is_visible_(false) {} + virtual ~TestTooltip() {} - // Fire tooltip timer so tooltip becomes visible. - generator_->MoveMouseRelativeTo(window, view_->bounds().CenterPoint()); - helper_->FireTooltipTimer(); - EXPECT_TRUE(helper_->IsTooltipVisible()); - for (int i = 0; i < 49; ++i) { - generator_->MoveMouseBy(1, 0); - EXPECT_TRUE(helper_->IsTooltipVisible()); - EXPECT_EQ(window, root_window->GetEventHandlerForPoint( - generator_->current_location())); - string16 expected_tooltip = - ASCIIToUTF16("Tooltip Text For RootWindow1"); - EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window)); - EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); - EXPECT_EQ(window, helper_->GetTooltipWindow()); + const base::string16& tooltip_text() const { return tooltip_text_; } + + // Tooltip: + virtual void SetText(aura::Window* window, + const base::string16& tooltip_text, + const gfx::Point& location) OVERRIDE { + tooltip_text_ = tooltip_text; + location_ = location; + } + virtual void Show() OVERRIDE { + is_visible_ = true; } + virtual void Hide() OVERRIDE { + is_visible_ = false; + } + virtual bool IsVisible() OVERRIDE { + return is_visible_; + } + const gfx::Point& location() { return location_; } + + private: + bool is_visible_; + base::string16 tooltip_text_; + gfx::Point location_; + + DISALLOW_COPY_AND_ASSIGN(TestTooltip); +}; + +} // namespace + +// Use for tests that don't depend upon views. +class TooltipControllerTest2 : public aura::test::AuraTestBase { + public: + TooltipControllerTest2() : test_tooltip_(new TestTooltip) {} + virtual ~TooltipControllerTest2() {} + + virtual void SetUp() OVERRIDE { + wm_state_.reset(new wm::WMState); + aura::test::AuraTestBase::SetUp(); + new wm::DefaultActivationClient(root_window()); + controller_.reset(new TooltipController( + scoped_ptr<corewm::Tooltip>(test_tooltip_))); + root_window()->AddPreTargetHandler(controller_.get()); + SetTooltipClient(root_window(), controller_.get()); + helper_.reset(new TooltipControllerTestHelper(controller_.get())); + generator_.reset(new aura::test::EventGenerator(root_window())); + } + + virtual void TearDown() OVERRIDE { + root_window()->RemovePreTargetHandler(controller_.get()); + aura::client::SetTooltipClient(root_window(), NULL); + controller_.reset(); + generator_.reset(); + helper_.reset(); + aura::test::AuraTestBase::TearDown(); + wm_state_.reset(); + } + + protected: + // Owned by |controller_|. + TestTooltip* test_tooltip_; + scoped_ptr<TooltipControllerTestHelper> helper_; + scoped_ptr<aura::test::EventGenerator> generator_; + + private: + scoped_ptr<TooltipController> controller_; + scoped_ptr<wm::WMState> wm_state_; + + DISALLOW_COPY_AND_ASSIGN(TooltipControllerTest2); +}; + +TEST_F(TooltipControllerTest2, VerifyLeadingTrailingWhitespaceStripped) { + aura::test::TestWindowDelegate test_delegate; + scoped_ptr<aura::Window> window( + CreateNormalWindow(100, root_window(), &test_delegate)); + window->SetBounds(gfx::Rect(0, 0, 300, 300)); + base::string16 tooltip_text(ASCIIToUTF16(" \nx ")); + aura::client::SetTooltipText(window.get(), &tooltip_text); + generator_->MoveMouseToCenterOf(window.get()); + helper_->FireTooltipTimer(); + EXPECT_EQ(ASCIIToUTF16("x"), test_tooltip_->tooltip_text()); +} + +// Verifies that tooltip is hidden and tooltip window closed upon cancel mode. +TEST_F(TooltipControllerTest2, CloseOnCancelMode) { + aura::test::TestWindowDelegate test_delegate; + scoped_ptr<aura::Window> window( + CreateNormalWindow(100, root_window(), &test_delegate)); + window->SetBounds(gfx::Rect(0, 0, 300, 300)); + base::string16 tooltip_text(ASCIIToUTF16("Tooltip Text")); + aura::client::SetTooltipText(window.get(), &tooltip_text); + generator_->MoveMouseToCenterOf(window.get()); - views::Widget* widget2 = CreateWidget(NULL); - widget2->SetContentsView(new View); - TooltipTestView* view2 = new TooltipTestView; - widget2->GetContentsView()->AddChildView(view2); - view2->SetBoundsRect(widget2->GetContentsView()->GetLocalBounds()); - helper_.reset(new TooltipControllerTestHelper( - GetController(widget2))); - generator_.reset(new aura::test::EventGenerator( - widget2->GetNativeWindow()->GetRootWindow())); - view2->set_tooltip_text(ASCIIToUTF16("Tooltip Text For RootWindow2")); - - aura::Window* window2 = widget2->GetNativeWindow(); - aura::Window* root_window2 = - widget2->GetNativeWindow()->GetRootWindow(); // Fire tooltip timer so tooltip becomes visible. - generator_->MoveMouseRelativeTo(window2, view2->bounds().CenterPoint()); helper_->FireTooltipTimer(); + EXPECT_TRUE(helper_->IsTooltipVisible()); - EXPECT_NE(root_window, root_window2); - EXPECT_NE(window, window2); + // Send OnCancelMode event and verify that tooltip becomes invisible and + // the tooltip window is closed. + ui::CancelModeEvent event; + helper_->controller()->OnCancelMode(&event); + EXPECT_FALSE(helper_->IsTooltipVisible()); + EXPECT_TRUE(helper_->GetTooltipWindow() == NULL); +} - for (int i = 0; i < 49; ++i) { - generator_->MoveMouseBy(1, 0); - EXPECT_TRUE(helper_->IsTooltipVisible()); - EXPECT_EQ(window2, root_window2->GetEventHandlerForPoint( - generator_->current_location())); - string16 expected_tooltip = ASCIIToUTF16("Tooltip Text For RootWindow2"); - EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(window2)); - EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); - EXPECT_EQ(window2, helper_->GetTooltipWindow()); +// Use for tests that need both views and a TestTooltip. +class TooltipControllerTest3 : public aura::test::AuraTestBase { + public: + TooltipControllerTest3() : test_tooltip_(new TestTooltip) {} + virtual ~TooltipControllerTest3() {} + + virtual void SetUp() OVERRIDE { + wm_state_.reset(new wm::WMState); + aura::test::AuraTestBase::SetUp(); + new wm::DefaultActivationClient(root_window()); + + widget_.reset(CreateWidget(root_window())); + widget_->SetContentsView(new View); + view_ = new TooltipTestView; + widget_->GetContentsView()->AddChildView(view_); + view_->SetBoundsRect(widget_->GetContentsView()->GetLocalBounds()); + + generator_.reset(new aura::test::EventGenerator(GetRootWindow())); + controller_.reset(new TooltipController( + scoped_ptr<views::corewm::Tooltip>(test_tooltip_))); + GetRootWindow()->RemovePreTargetHandler( + static_cast<TooltipController*>(aura::client::GetTooltipClient( + widget_->GetNativeWindow()->GetRootWindow()))); + GetRootWindow()->AddPreTargetHandler(controller_.get()); + helper_.reset(new TooltipControllerTestHelper(controller_.get())); + SetTooltipClient(GetRootWindow(), controller_.get()); } - bool tooltip_reparented = false; - for (size_t i = 0; i < root_window2->children().size(); ++i) { - if (root_window2->children()[i]->type() == - aura::client::WINDOW_TYPE_TOOLTIP) { - tooltip_reparented = true; - break; - } + virtual void TearDown() OVERRIDE { + GetRootWindow()->RemovePreTargetHandler(controller_.get()); + aura::client::SetTooltipClient(GetRootWindow(), NULL); + + controller_.reset(); + generator_.reset(); + helper_.reset(); + widget_.reset(); + aura::test::AuraTestBase::TearDown(); + wm_state_.reset(); } - EXPECT_TRUE(tooltip_reparented); - widget2->Close(); -} -// This test validates whether the tooltip after becoming visible stays at the -// top of the ZOrder in its root window after activation changes. -TEST_F(TooltipControllerTest, TooltipAtTopOfZOrderAfterActivation) { - view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text")); - EXPECT_EQ(string16(), helper_->GetTooltipText()); - EXPECT_EQ(NULL, helper_->GetTooltipWindow()); - generator_->MoveMouseToCenterOf(GetWindow()); + aura::Window* GetWindow() { return widget_->GetNativeWindow(); } - EXPECT_EQ(GetWindow(), GetRootWindow()->GetEventHandlerForPoint( - generator_->current_location())); - string16 expected_tooltip = ASCIIToUTF16("Tooltip Text"); - EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(GetWindow())); - EXPECT_EQ(string16(), helper_->GetTooltipText()); - EXPECT_EQ(GetWindow(), helper_->GetTooltipWindow()); + protected: + // Owned by |controller_|. + TestTooltip* test_tooltip_; + scoped_ptr<TooltipControllerTestHelper> helper_; + scoped_ptr<aura::test::EventGenerator> generator_; + scoped_ptr<views::Widget> widget_; + TooltipTestView* view_; - // Fire tooltip timer so tooltip becomes visible. + private: + scoped_ptr<TooltipController> controller_; + scoped_ptr<wm::WMState> wm_state_; + +#if defined(OS_WIN) + ui::ScopedOleInitializer ole_initializer_; +#endif + + aura::Window* GetRootWindow() { return GetWindow()->GetRootWindow(); } + + DISALLOW_COPY_AND_ASSIGN(TooltipControllerTest3); +}; + +TEST_F(TooltipControllerTest3, TooltipPositionChangesOnTwoViewsWithSameLabel) { + // Owned by |view_|. + // These two views have the same tooltip text + TooltipTestView* v1 = new TooltipTestView; + TooltipTestView* v2 = new TooltipTestView; + // v1_1 is a view inside v1 that has an identical tooltip text to that of v1 + // and v2 + TooltipTestView* v1_1 = new TooltipTestView; + // v2_1 is a view inside v2 that has an identical tooltip text to that of v1 + // and v2 + TooltipTestView* v2_1 = new TooltipTestView; + // v2_2 is a view inside v2 with the tooltip text different from all the + // others + TooltipTestView* v2_2 = new TooltipTestView; + + // Setup all the views' relations + view_->AddChildView(v1); + view_->AddChildView(v2); + v1->AddChildView(v1_1); + v2->AddChildView(v2_1); + v2->AddChildView(v2_2); + const base::string16 reference_string( + base::ASCIIToUTF16("Identical Tooltip Text")); + const base::string16 alternative_string( + base::ASCIIToUTF16("Another Shrubbery")); + v1->set_tooltip_text(reference_string); + v2->set_tooltip_text(reference_string); + v1_1->set_tooltip_text(reference_string); + v2_1->set_tooltip_text(reference_string); + v2_2->set_tooltip_text(alternative_string); + + // Set views' bounds + gfx::Rect view_bounds(view_->GetLocalBounds()); + view_bounds.set_height(view_bounds.height() / 2); + v1->SetBoundsRect(view_bounds); + v1_1->SetBounds(0, 0, 3, 3); + view_bounds.set_y(view_bounds.height()); + v2->SetBoundsRect(view_bounds); + v2_2->SetBounds(view_bounds.width() - 3, view_bounds.height() - 3, 3, 3); + v2_1->SetBounds(0, 0, 3, 3); + + // Test whether a toolbar appears on v1 + gfx::Point center = v1->bounds().CenterPoint(); + generator_->MoveMouseRelativeTo(GetWindow(), center); helper_->FireTooltipTimer(); + EXPECT_TRUE(helper_->IsTooltipVisible()); + EXPECT_EQ(reference_string, helper_->GetTooltipText()); + gfx::Point tooltip_bounds1 = test_tooltip_->location(); + // Test whether the toolbar changes position on mouse over v2 + center = v2->bounds().CenterPoint(); + generator_->MoveMouseRelativeTo(GetWindow(), center); + helper_->FireTooltipTimer(); EXPECT_TRUE(helper_->IsTooltipVisible()); - generator_->MoveMouseBy(1, 0); + EXPECT_EQ(reference_string, helper_->GetTooltipText()); + gfx::Point tooltip_bounds2 = test_tooltip_->location(); + + EXPECT_NE(tooltip_bounds1, gfx::Point()); + EXPECT_NE(tooltip_bounds2, gfx::Point()); + EXPECT_NE(tooltip_bounds1, tooltip_bounds2); + + // Test if the toolbar does not change position on encountering a contained + // view with the same tooltip text + center = v2_1->GetLocalBounds().CenterPoint(); + views::View::ConvertPointToTarget(v2_1, view_, ¢er); + generator_->MoveMouseRelativeTo(GetWindow(), center); + helper_->FireTooltipTimer(); + gfx::Point tooltip_bounds2_1 = test_tooltip_->location(); + EXPECT_NE(tooltip_bounds2, tooltip_bounds2_1); EXPECT_TRUE(helper_->IsTooltipVisible()); - EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(GetWindow())); - EXPECT_EQ(expected_tooltip, helper_->GetTooltipText()); - EXPECT_EQ(GetWindow(), helper_->GetTooltipWindow()); + EXPECT_EQ(reference_string, helper_->GetTooltipText()); + + // Test if the toolbar changes position on encountering a contained + // view with a different tooltip text + center = v2_2->GetLocalBounds().CenterPoint(); + views::View::ConvertPointToTarget(v2_2, view_, ¢er); + generator_->MoveMouseRelativeTo(GetWindow(), center); + helper_->FireTooltipTimer(); + gfx::Point tooltip_bounds2_2 = test_tooltip_->location(); - // Fake activation loss and gain in the native widget. This should cause a - // ZOrder change which should not affect the position of the tooltip. - DesktopNativeWidgetAura* native_widget = - static_cast<DesktopNativeWidgetAura*>(widget_->native_widget()); - EXPECT_TRUE(native_widget != NULL); + EXPECT_NE(tooltip_bounds2_1, tooltip_bounds2_2); + EXPECT_TRUE(helper_->IsTooltipVisible()); + EXPECT_EQ(alternative_string, helper_->GetTooltipText()); - native_widget->HandleActivationChanged(false); - native_widget->HandleActivationChanged(true); + // Test if moving from a view that is contained by a larger view, both with + // the same tooltip text, does not change tooltip's position. + center = v1_1->GetLocalBounds().CenterPoint(); + views::View::ConvertPointToTarget(v1_1, view_, ¢er); + generator_->MoveMouseRelativeTo(GetWindow(), center); + helper_->FireTooltipTimer(); + gfx::Point tooltip_bounds1_1 = test_tooltip_->location(); - EXPECT_EQ( - widget_->GetNativeWindow()->GetRootWindow()->children().back()->type(), - aura::client::WINDOW_TYPE_TOOLTIP); -} + EXPECT_TRUE(helper_->IsTooltipVisible()); + EXPECT_EQ(reference_string, helper_->GetTooltipText()); -#endif + center = v1->bounds().CenterPoint(); + generator_->MoveMouseRelativeTo(GetWindow(), center); + helper_->FireTooltipTimer(); + tooltip_bounds1 = test_tooltip_->location(); + + EXPECT_NE(tooltip_bounds1_1, tooltip_bounds1); + EXPECT_EQ(reference_string, helper_->GetTooltipText()); +} } // namespace test } // namespace corewm diff --git a/chromium/ui/views/corewm/tooltip_win.cc b/chromium/ui/views/corewm/tooltip_win.cc index 10341d61635..4d2a4badb0e 100644 --- a/chromium/ui/views/corewm/tooltip_win.cc +++ b/chromium/ui/views/corewm/tooltip_win.cc @@ -12,6 +12,8 @@ #include "ui/base/l10n/l10n_util_win.h" #include "ui/gfx/rect.h" #include "ui/gfx/screen.h" +#include "ui/gfx/win/dpi.h" +#include "ui/views/corewm/cursor_height_provider_win.h" namespace views { namespace corewm { @@ -63,7 +65,7 @@ bool TooltipWin::EnsureTooltipWindow() { TOOLTIPS_CLASS, NULL, TTS_NOPREFIX | WS_POPUP, 0, 0, 0, 0, parent_hwnd_, NULL, NULL, NULL); if (!tooltip_hwnd_) { - LOG_GETLASTERROR(WARNING) << "tooltip creation failed, disabling tooltips"; + PLOG(WARNING) << "tooltip creation failed, disabling tooltips"; return false; } @@ -76,20 +78,19 @@ bool TooltipWin::EnsureTooltipWindow() { void TooltipWin::PositionTooltip() { // This code only runs for non-metro, so GetNativeScreen() is fine. - gfx::Display display( - gfx::Screen::GetNativeScreen()->GetDisplayNearestPoint(location_)); + gfx::Point screen_point = gfx::win::DIPToScreenPoint(location_); + const int cursoroffset = GetCurrentCursorVisibleHeight(); + screen_point.Offset(0, cursoroffset); DWORD tooltip_size = SendMessage(tooltip_hwnd_, TTM_GETBUBBLESIZE, 0, reinterpret_cast<LPARAM>(&toolinfo_)); - // 20 accounts for visible cursor size. I tried using SM_CYCURSOR but that's - // way too big (32 on win7 default). - // TODO(sky): figure out the right way to determine offset. - const int initial_y = location_.y(); - gfx::Rect tooltip_bounds(location_.x(), initial_y + 20, - LOWORD(tooltip_size), HIWORD(tooltip_size)); - tooltip_bounds.AdjustToFit(display.work_area()); - if (tooltip_bounds.y() < initial_y) - tooltip_bounds.set_y(initial_y - tooltip_bounds.height() - 2); + const gfx::Size size(LOWORD(tooltip_size), HIWORD(tooltip_size)); + + const gfx::Display display( + gfx::Screen::GetNativeScreen()->GetDisplayNearestPoint(screen_point)); + + gfx::Rect tooltip_bounds(screen_point, size); + tooltip_bounds.AdjustToFit(gfx::win::DIPToScreenRect(display.work_area())); SetWindowPos(tooltip_hwnd_, NULL, tooltip_bounds.x(), tooltip_bounds.y(), 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); } @@ -116,8 +117,9 @@ void TooltipWin::SetText(aura::Window* window, reinterpret_cast<LPARAM>(&toolinfo_)); // This code only runs for non-metro, so GetNativeScreen() is fine. + const gfx::Point screen_point = gfx::win::DIPToScreenPoint(location_); gfx::Display display( - gfx::Screen::GetNativeScreen()->GetDisplayNearestPoint(location_)); + gfx::Screen::GetNativeScreen()->GetDisplayNearestPoint(screen_point)); const gfx::Rect monitor_bounds = display.bounds(); int max_width = (monitor_bounds.width() + 1) / 2; SendMessage(tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0, max_width); @@ -134,7 +136,7 @@ void TooltipWin::Show() { } void TooltipWin::Hide() { - if (!EnsureTooltipWindow()) + if (!tooltip_hwnd_) return; SendMessage(tooltip_hwnd_, TTM_TRACKACTIVATE, FALSE, diff --git a/chromium/ui/views/corewm/tooltip_win.h b/chromium/ui/views/corewm/tooltip_win.h index e454aac8773..b031a3204eb 100644 --- a/chromium/ui/views/corewm/tooltip_win.h +++ b/chromium/ui/views/corewm/tooltip_win.h @@ -24,7 +24,7 @@ class VIEWS_EXPORT TooltipWin : public Tooltip { explicit TooltipWin(HWND parent); virtual ~TooltipWin(); - // HandleNotify() is forwarded from DesktopRootWindowHostWin to keep the + // HandleNotify() is forwarded from DesktopWindowTreeHostWin to keep the // native tooltip in sync. bool HandleNotify(int w_param, NMHDR* l_param, LRESULT* l_result); diff --git a/chromium/ui/views/corewm/transient_window_stacking_client.cc b/chromium/ui/views/corewm/transient_window_stacking_client.cc deleted file mode 100644 index 6a4f8ab25cb..00000000000 --- a/chromium/ui/views/corewm/transient_window_stacking_client.cc +++ /dev/null @@ -1,95 +0,0 @@ -// 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/corewm/transient_window_stacking_client.h" - -#include <algorithm> - -using aura::Window; - -namespace views { -namespace corewm { - -namespace { - -// Populates |ancestors| with all transient ancestors of |window| that are -// siblings of |window|. Returns true if any ancestors were found, false if not. -bool GetAllTransientAncestors(Window* window, Window::Windows* ancestors) { - Window* parent = window->parent(); - for (; window; window = window->transient_parent()) { - if (window->parent() == parent) - ancestors->push_back(window); - } - return (!ancestors->empty()); -} - -// Replaces |window1| and |window2| with their possible transient ancestors that -// are still siblings (have a common transient parent). |window1| and |window2| -// are not modified if such ancestors cannot be found. -void FindCommonTransientAncestor(Window** window1, Window** window2) { - DCHECK(window1); - DCHECK(window2); - DCHECK(*window1); - DCHECK(*window2); - // Assemble chains of ancestors of both windows. - Window::Windows ancestors1; - Window::Windows ancestors2; - if (!GetAllTransientAncestors(*window1, &ancestors1) || - !GetAllTransientAncestors(*window2, &ancestors2)) { - return; - } - // Walk the two chains backwards and look for the first difference. - Window::Windows::const_reverse_iterator it1 = ancestors1.rbegin(); - Window::Windows::const_reverse_iterator it2 = ancestors2.rbegin(); - for (; it1 != ancestors1.rend() && it2 != ancestors2.rend(); ++it1, ++it2) { - if (*it1 != *it2) { - *window1 = *it1; - *window2 = *it2; - break; - } - } -} - -// Returns true if |window| has |ancestor| as a transient ancestor. A transient -// ancestor is found by following the transient parent chain of the window. -bool HasTransientAncestor(const Window* window, const Window* ancestor) { - if (window->transient_parent() == ancestor) - return true; - return window->transient_parent() ? - HasTransientAncestor(window->transient_parent(), ancestor) : false; -} - -} // namespace - -TransientWindowStackingClient::TransientWindowStackingClient() { -} - -TransientWindowStackingClient::~TransientWindowStackingClient() { -} - -void TransientWindowStackingClient::AdjustStacking( - Window** child, - Window** target, - Window::StackDirection* direction) { - // For windows that have transient children stack the transient ancestors that - // are siblings. This prevents one transient group from being inserted in the - // middle of another. - FindCommonTransientAncestor(child, target); - - // When stacking above skip to the topmost transient descendant of the target. - if (*direction == Window::STACK_ABOVE && - !HasTransientAncestor(*child, *target)) { - const Window::Windows& siblings((*child)->parent()->children()); - size_t target_i = - std::find(siblings.begin(), siblings.end(), *target) - siblings.begin(); - while (target_i + 1 < siblings.size() && - HasTransientAncestor(siblings[target_i + 1], *target)) { - ++target_i; - } - *target = siblings[target_i]; - } -} - -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/transient_window_stacking_client.h b/chromium/ui/views/corewm/transient_window_stacking_client.h deleted file mode 100644 index 19358362bcb..00000000000 --- a/chromium/ui/views/corewm/transient_window_stacking_client.h +++ /dev/null @@ -1,32 +0,0 @@ -// 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_COREWM_TRANSIENT_WINDOW_STACKING_CLIENT_H_ -#define UI_VIEWS_COREWM_TRANSIENT_WINDOW_STACKING_CLIENT_H_ - -#include "ui/aura/client/window_stacking_client.h" -#include "ui/views/views_export.h" - -namespace views { -namespace corewm { - -class VIEWS_EXPORT TransientWindowStackingClient - : public aura::client::WindowStackingClient { - public: - TransientWindowStackingClient(); - virtual ~TransientWindowStackingClient(); - - // WindowStackingClient: - virtual void AdjustStacking(aura::Window** child, - aura::Window** target, - aura::Window::StackDirection* direction) OVERRIDE; - - private: - DISALLOW_COPY_AND_ASSIGN(TransientWindowStackingClient); -}; - -} // namespace corewm -} // namespace views - -#endif // UI_VIEWS_COREWM_TRANSIENT_WINDOW_STACKING_CLIENT_H_ diff --git a/chromium/ui/views/corewm/transient_window_stacking_client_unittest.cc b/chromium/ui/views/corewm/transient_window_stacking_client_unittest.cc deleted file mode 100644 index a84b832e596..00000000000 --- a/chromium/ui/views/corewm/transient_window_stacking_client_unittest.cc +++ /dev/null @@ -1,175 +0,0 @@ -// 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/corewm/transient_window_stacking_client.h" - -#include "base/memory/scoped_ptr.h" -#include "ui/aura/test/aura_test_base.h" -#include "ui/aura/test/test_windows.h" - -using aura::test::ChildWindowIDsAsString; -using aura::test::CreateTestWindowWithId; -using aura::Window; - -namespace views { -namespace corewm { - -class TransientWindowStackingClientTest : public aura::test::AuraTestBase { - public: - TransientWindowStackingClientTest() {} - virtual ~TransientWindowStackingClientTest() {} - - virtual void SetUp() OVERRIDE { - AuraTestBase::SetUp(); - aura::client::SetWindowStackingClient(new TransientWindowStackingClient); - } - - virtual void TearDown() OVERRIDE { - aura::client::SetWindowStackingClient(NULL); - AuraTestBase::TearDown(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(TransientWindowStackingClientTest); -}; - -// Tests that transient children are stacked as a unit when using stack above. -TEST_F(TransientWindowStackingClientTest, TransientChildrenGroupAbove) { - scoped_ptr<Window> parent(CreateTestWindowWithId(0, root_window())); - scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get())); - Window* w11 = CreateTestWindowWithId(11, parent.get()); - scoped_ptr<Window> w2(CreateTestWindowWithId(2, parent.get())); - Window* w21 = CreateTestWindowWithId(21, parent.get()); - Window* w211 = CreateTestWindowWithId(211, parent.get()); - Window* w212 = CreateTestWindowWithId(212, parent.get()); - Window* w213 = CreateTestWindowWithId(213, parent.get()); - Window* w22 = CreateTestWindowWithId(22, parent.get()); - ASSERT_EQ(8u, parent->children().size()); - - w1->AddTransientChild(w11); // w11 is now owned by w1. - w2->AddTransientChild(w21); // w21 is now owned by w2. - w2->AddTransientChild(w22); // w22 is now owned by w2. - w21->AddTransientChild(w211); // w211 is now owned by w21. - w21->AddTransientChild(w212); // w212 is now owned by w21. - w21->AddTransientChild(w213); // w213 is now owned by w21. - EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); - - // Stack w1 at the top (end), this should force w11 to be last (on top of w1). - parent->StackChildAtTop(w1.get()); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get())); - - // This tests that the order in children_ array rather than in - // transient_children_ array is used when reinserting transient children. - // If transient_children_ array was used '22' would be following '21'. - parent->StackChildAtTop(w2.get()); - EXPECT_EQ(w22, parent->children().back()); - EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); - - parent->StackChildAbove(w11, w2.get()); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get())); - - parent->StackChildAbove(w21, w1.get()); - EXPECT_EQ(w22, parent->children().back()); - EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); - - parent->StackChildAbove(w21, w22); - EXPECT_EQ(w213, parent->children().back()); - EXPECT_EQ("1 11 2 22 21 211 212 213", ChildWindowIDsAsString(parent.get())); - - parent->StackChildAbove(w11, w21); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 22 21 211 212 213 1 11", ChildWindowIDsAsString(parent.get())); - - parent->StackChildAbove(w213, w21); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); - - // No change when stacking a transient parent above its transient child. - parent->StackChildAbove(w21, w211); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); - - // This tests that the order in children_ array rather than in - // transient_children_ array is used when reinserting transient children. - // If transient_children_ array was used '22' would be following '21'. - parent->StackChildAbove(w2.get(), w1.get()); - EXPECT_EQ(w212, parent->children().back()); - EXPECT_EQ("1 11 2 22 21 213 211 212", ChildWindowIDsAsString(parent.get())); - - parent->StackChildAbove(w11, w213); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); -} - -// Tests that transient children are stacked as a unit when using stack below. -TEST_F(TransientWindowStackingClientTest, TransientChildrenGroupBelow) { - scoped_ptr<Window> parent(CreateTestWindowWithId(0, root_window())); - scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get())); - Window* w11 = CreateTestWindowWithId(11, parent.get()); - scoped_ptr<Window> w2(CreateTestWindowWithId(2, parent.get())); - Window* w21 = CreateTestWindowWithId(21, parent.get()); - Window* w211 = CreateTestWindowWithId(211, parent.get()); - Window* w212 = CreateTestWindowWithId(212, parent.get()); - Window* w213 = CreateTestWindowWithId(213, parent.get()); - Window* w22 = CreateTestWindowWithId(22, parent.get()); - ASSERT_EQ(8u, parent->children().size()); - - w1->AddTransientChild(w11); // w11 is now owned by w1. - w2->AddTransientChild(w21); // w21 is now owned by w2. - w2->AddTransientChild(w22); // w22 is now owned by w2. - w21->AddTransientChild(w211); // w211 is now owned by w21. - w21->AddTransientChild(w212); // w212 is now owned by w21. - w21->AddTransientChild(w213); // w213 is now owned by w21. - EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); - - // Stack w2 at the bottom, this should force w11 to be last (on top of w1). - // This also tests that the order in children_ array rather than in - // transient_children_ array is used when reinserting transient children. - // If transient_children_ array was used '22' would be following '21'. - parent->StackChildAtBottom(w2.get()); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get())); - - parent->StackChildAtBottom(w1.get()); - EXPECT_EQ(w22, parent->children().back()); - EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); - - parent->StackChildBelow(w21, w1.get()); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get())); - - parent->StackChildBelow(w11, w2.get()); - EXPECT_EQ(w22, parent->children().back()); - EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); - - parent->StackChildBelow(w22, w21); - EXPECT_EQ(w213, parent->children().back()); - EXPECT_EQ("1 11 2 22 21 211 212 213", ChildWindowIDsAsString(parent.get())); - - parent->StackChildBelow(w21, w11); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 22 21 211 212 213 1 11", ChildWindowIDsAsString(parent.get())); - - parent->StackChildBelow(w213, w211); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); - - // No change when stacking a transient parent below its transient child. - parent->StackChildBelow(w21, w211); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); - - parent->StackChildBelow(w1.get(), w2.get()); - EXPECT_EQ(w212, parent->children().back()); - EXPECT_EQ("1 11 2 22 21 213 211 212", ChildWindowIDsAsString(parent.get())); - - parent->StackChildBelow(w213, w11); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); -} - -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/visibility_controller.cc b/chromium/ui/views/corewm/visibility_controller.cc deleted file mode 100644 index 4542f41c669..00000000000 --- a/chromium/ui/views/corewm/visibility_controller.cc +++ /dev/null @@ -1,87 +0,0 @@ -// 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/corewm/visibility_controller.h" - -#include "ui/aura/window.h" -#include "ui/aura/window_property.h" -#include "ui/compositor/layer.h" -#include "ui/views/corewm/window_animations.h" - -namespace views { -namespace corewm { - -namespace { - -// Property set on all windows whose child windows' visibility changes are -// animated. -DEFINE_WINDOW_PROPERTY_KEY( - bool, kChildWindowVisibilityChangesAnimatedKey, false); - -bool ShouldAnimateWindow(aura::Window* window) { - return window->parent() && window->parent()->GetProperty( - kChildWindowVisibilityChangesAnimatedKey); -} - -} // namespace - -VisibilityController::VisibilityController() { -} - -VisibilityController::~VisibilityController() { -} - -bool VisibilityController::CallAnimateOnChildWindowVisibilityChanged( - aura::Window* window, - bool visible) { - return AnimateOnChildWindowVisibilityChanged(window, visible); -} - -void VisibilityController::UpdateLayerVisibility(aura::Window* window, - bool visible) { - bool animated = window->type() != aura::client::WINDOW_TYPE_CONTROL && - window->type() != aura::client::WINDOW_TYPE_UNKNOWN && - ShouldAnimateWindow(window); - animated = animated && - CallAnimateOnChildWindowVisibilityChanged(window, visible); - - if (!visible) { - // For window hiding animation, we want to check if the window is already - // animating, and not do SetVisible(false) if it is. - // TODO(vollick): remove this. - animated = animated || (window->layer()->GetAnimator()-> - IsAnimatingProperty(ui::LayerAnimationElement::OPACITY) && - window->layer()->GetTargetOpacity() == 0.0f); - } - - // When a window is made visible, we always make its layer visible - // immediately. When a window is hidden, the layer must be left visible and - // only made not visible once the animation is complete. - if (!animated || visible) - window->layer()->SetVisible(visible); -} - -SuspendChildWindowVisibilityAnimations::SuspendChildWindowVisibilityAnimations( - aura::Window* window) - : window_(window), - original_enabled_(window->GetProperty( - kChildWindowVisibilityChangesAnimatedKey)) { - window_->ClearProperty(kChildWindowVisibilityChangesAnimatedKey); -} - -SuspendChildWindowVisibilityAnimations:: - ~SuspendChildWindowVisibilityAnimations() { - if (original_enabled_) - window_->SetProperty(kChildWindowVisibilityChangesAnimatedKey, true); - else - window_->ClearProperty(kChildWindowVisibilityChangesAnimatedKey); -} - -void SetChildWindowVisibilityChangesAnimated(aura::Window* window) { - window->SetProperty(kChildWindowVisibilityChangesAnimatedKey, true); -} - -} // namespace corewm -} // namespace views - diff --git a/chromium/ui/views/corewm/visibility_controller.h b/chromium/ui/views/corewm/visibility_controller.h deleted file mode 100644 index ec489822f66..00000000000 --- a/chromium/ui/views/corewm/visibility_controller.h +++ /dev/null @@ -1,75 +0,0 @@ -// 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_COREWM_VISIBILITY_CONTROLLER_H_ -#define UI_VIEWS_COREWM_VISIBILITY_CONTROLLER_H_ - -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "ui/aura/client/visibility_client.h" -#include "ui/views/views_export.h" - -namespace views { -namespace corewm { - -class VIEWS_EXPORT VisibilityController - : public aura::client::VisibilityClient { - public: - VisibilityController(); - virtual ~VisibilityController(); - - protected: - // Subclasses override if they want to call a different implementation of - // this function. - // TODO(beng): potentially replace by an actual window animator class in - // window_animations.h. - virtual bool CallAnimateOnChildWindowVisibilityChanged(aura::Window* window, - bool visible); - - private: - // Overridden from aura::client::VisibilityClient: - virtual void UpdateLayerVisibility(aura::Window* window, - bool visible) OVERRIDE; - - DISALLOW_COPY_AND_ASSIGN(VisibilityController); -}; - -// Suspends the animations for visibility changes during the lifetime of an -// instance of this class. -// -// Example: -// -// void ViewName::UnanimatedAction() { -// SuspendChildWindowVisibilityAnimations suspend(parent); -// // Perform unanimated action here. -// // ... -// // When the method finishes, visibility animations will return to their -// // previous state. -// } -// -class VIEWS_EXPORT SuspendChildWindowVisibilityAnimations { - public: - // Suspend visibility animations of child windows. - explicit SuspendChildWindowVisibilityAnimations(aura::Window* window); - - // Restore visibility animations to their original state. - ~SuspendChildWindowVisibilityAnimations(); - - private: - // The window to manage. - aura::Window* window_; - - // Whether the visibility animations on child windows were originally enabled. - const bool original_enabled_; - - DISALLOW_COPY_AND_ASSIGN(SuspendChildWindowVisibilityAnimations); -}; - -// Tells |window| to animate visibility changes to its children. -void VIEWS_EXPORT SetChildWindowVisibilityChangesAnimated(aura::Window* window); - -} // namespace corewm -} // namespace views - -#endif // UI_VIEWS_COREWM_VISIBILITY_CONTROLLER_H_ diff --git a/chromium/ui/views/corewm/visibility_controller_unittest.cc b/chromium/ui/views/corewm/visibility_controller_unittest.cc deleted file mode 100644 index 222d7af61e8..00000000000 --- a/chromium/ui/views/corewm/visibility_controller_unittest.cc +++ /dev/null @@ -1,57 +0,0 @@ -// 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/corewm/visibility_controller.h" - -#include "ui/aura/root_window.h" -#include "ui/aura/test/aura_test_base.h" -#include "ui/aura/test/test_window_delegate.h" -#include "ui/aura/test/test_windows.h" -#include "ui/aura/window.h" -#include "ui/compositor/layer.h" -#include "ui/compositor/layer_animator.h" -#include "ui/compositor/scoped_animation_duration_scale_mode.h" - -namespace views { -namespace corewm { - -typedef aura::test::AuraTestBase VisibilityControllerTest; - -// Hiding a window in an animatable container should not hide the window's layer -// immediately. -TEST_F(VisibilityControllerTest, AnimateHideDoesntHideWindowLayer) { - // We cannot disable animations for this test. - ui::ScopedAnimationDurationScaleMode normal_duration_mode( - ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); - - VisibilityController controller; - aura::client::SetVisibilityClient(root_window(), &controller); - - SetChildWindowVisibilityChangesAnimated(root_window()); - - aura::test::TestWindowDelegate d; - scoped_ptr<aura::Window> animatable( - aura::test::CreateTestWindowWithDelegate( - &d, -2, gfx::Rect(0, 0, 50, 50), root_window())); - scoped_ptr<aura::Window> non_animatable( - aura::test::CreateTestWindowWithDelegateAndType( - &d, aura::client::WINDOW_TYPE_CONTROL, -3, gfx::Rect(51, 51, 50, 50), - root_window())); - EXPECT_TRUE(animatable->IsVisible()); - EXPECT_TRUE(animatable->layer()->visible()); - animatable->Hide(); - EXPECT_FALSE(animatable->IsVisible()); - EXPECT_TRUE(animatable->layer()->visible()); - - EXPECT_TRUE(non_animatable->IsVisible()); - EXPECT_TRUE(non_animatable->layer()->visible()); - non_animatable->Hide(); - EXPECT_FALSE(non_animatable->IsVisible()); - EXPECT_FALSE(non_animatable->layer()->visible()); - - aura::client::SetVisibilityClient(root_window(), NULL); -} - -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/window_animations.cc b/chromium/ui/views/corewm/window_animations.cc deleted file mode 100644 index 736f4bba822..00000000000 --- a/chromium/ui/views/corewm/window_animations.cc +++ /dev/null @@ -1,573 +0,0 @@ -// 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/corewm/window_animations.h" - -#include <math.h> - -#include <algorithm> -#include <vector> - -#include "base/command_line.h" -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "base/message_loop/message_loop.h" -#include "base/stl_util.h" -#include "base/time/time.h" -#include "ui/aura/client/animation_host.h" -#include "ui/aura/client/aura_constants.h" -#include "ui/aura/window.h" -#include "ui/aura/window_delegate.h" -#include "ui/aura/window_observer.h" -#include "ui/aura/window_property.h" -#include "ui/compositor/compositor_observer.h" -#include "ui/compositor/layer.h" -#include "ui/compositor/layer_animation_observer.h" -#include "ui/compositor/layer_animation_sequence.h" -#include "ui/compositor/layer_animator.h" -#include "ui/compositor/scoped_layer_animation_settings.h" -#include "ui/gfx/interpolated_transform.h" -#include "ui/gfx/rect_conversions.h" -#include "ui/gfx/screen.h" -#include "ui/gfx/vector2d.h" -#include "ui/gfx/vector3d_f.h" -#include "ui/views/corewm/corewm_switches.h" -#include "ui/views/corewm/window_util.h" -#include "ui/views/view.h" -#include "ui/views/widget/widget.h" - -DECLARE_WINDOW_PROPERTY_TYPE(int) -DECLARE_WINDOW_PROPERTY_TYPE(views::corewm::WindowVisibilityAnimationType) -DECLARE_WINDOW_PROPERTY_TYPE(views::corewm::WindowVisibilityAnimationTransition) -DECLARE_WINDOW_PROPERTY_TYPE(float) -DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(VIEWS_EXPORT, bool) - -using aura::Window; -using base::TimeDelta; -using ui::Layer; - -namespace views { -namespace corewm { -namespace { -const float kWindowAnimation_Vertical_TranslateY = 15.f; -} // namespace - -DEFINE_WINDOW_PROPERTY_KEY(int, - kWindowVisibilityAnimationTypeKey, - WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT); -DEFINE_WINDOW_PROPERTY_KEY(int, kWindowVisibilityAnimationDurationKey, 0); -DEFINE_WINDOW_PROPERTY_KEY(WindowVisibilityAnimationTransition, - kWindowVisibilityAnimationTransitionKey, - ANIMATE_BOTH); -DEFINE_WINDOW_PROPERTY_KEY(float, - kWindowVisibilityAnimationVerticalPositionKey, - kWindowAnimation_Vertical_TranslateY); - -namespace { - -const int kDefaultAnimationDurationForMenuMS = 150; - -const float kWindowAnimation_HideOpacity = 0.f; -const float kWindowAnimation_ShowOpacity = 1.f; -const float kWindowAnimation_TranslateFactor = 0.5f; -const float kWindowAnimation_ScaleFactor = .95f; - -const int kWindowAnimation_Rotate_DurationMS = 180; -const int kWindowAnimation_Rotate_OpacityDurationPercent = 90; -const float kWindowAnimation_Rotate_TranslateY = -20.f; -const float kWindowAnimation_Rotate_PerspectiveDepth = 500.f; -const float kWindowAnimation_Rotate_DegreesX = 5.f; -const float kWindowAnimation_Rotate_ScaleFactor = .99f; - -const float kWindowAnimation_Bounce_Scale = 1.02f; -const int kWindowAnimation_Bounce_DurationMS = 180; -const int kWindowAnimation_Bounce_GrowShrinkDurationPercent = 40; - -base::TimeDelta GetWindowVisibilityAnimationDuration(aura::Window* window) { - int duration = - window->GetProperty(kWindowVisibilityAnimationDurationKey); - if (duration == 0 && window->type() == aura::client::WINDOW_TYPE_MENU) { - return base::TimeDelta::FromMilliseconds( - kDefaultAnimationDurationForMenuMS); - } - return TimeDelta::FromInternalValue(duration); -} - -// Gets/sets the WindowVisibilityAnimationType associated with a window. -// TODO(beng): redundant/fold into method on public api? -int GetWindowVisibilityAnimationType(aura::Window* window) { - int type = window->GetProperty(kWindowVisibilityAnimationTypeKey); - if (type == WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT) { - return (window->type() == aura::client::WINDOW_TYPE_MENU || - window->type() == aura::client::WINDOW_TYPE_TOOLTIP) ? - WINDOW_VISIBILITY_ANIMATION_TYPE_FADE : - WINDOW_VISIBILITY_ANIMATION_TYPE_DROP; - } - return type; -} - -// Observes a hide animation. -// A window can be hidden for a variety of reasons. Sometimes, Hide() will be -// called and life is simple. Sometimes, the window is actually bound to a -// views::Widget and that Widget is closed, and life is a little more -// complicated. When a Widget is closed the aura::Window* is actually not -// destroyed immediately - it is actually just immediately hidden and then -// destroyed when the stack unwinds. To handle this case, we start the hide -// animation immediately when the window is hidden, then when the window is -// subsequently destroyed this object acquires ownership of the window's layer, -// so that it can continue animating it until the animation completes. -// Regardless of whether or not the window is destroyed, this object deletes -// itself when the animation completes. -class HidingWindowAnimationObserver : public ui::ImplicitAnimationObserver, - public aura::WindowObserver { - public: - explicit HidingWindowAnimationObserver(aura::Window* window) - : window_(window) { - window_->AddObserver(this); - } - virtual ~HidingWindowAnimationObserver() { - STLDeleteElements(&layers_); - } - - private: - // Overridden from ui::ImplicitAnimationObserver: - virtual void OnImplicitAnimationsCompleted() OVERRIDE { - // Window may have been destroyed by this point. - if (window_) { - aura::client::AnimationHost* animation_host = - aura::client::GetAnimationHost(window_); - if (animation_host) - animation_host->OnWindowHidingAnimationCompleted(); - window_->RemoveObserver(this); - } - delete this; - } - - // Overridden from aura::WindowObserver: - virtual void OnWindowDestroying(aura::Window* window) OVERRIDE { - DCHECK_EQ(window, window_); - DCHECK(layers_.empty()); - AcquireAllLayers(window_); - - // If the Widget has views with layers, then it is necessary to take - // ownership of those layers too. - views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window_); - const views::Widget* const_widget = widget; - if (widget && const_widget->GetRootView() && widget->GetContentsView()) - AcquireAllViewLayers(widget->GetContentsView()); - window_->RemoveObserver(this); - window_ = NULL; - } - - void AcquireAllLayers(aura::Window* window) { - ui::Layer* layer = window->AcquireLayer(); - DCHECK(layer); - layers_.push_back(layer); - for (aura::Window::Windows::const_iterator it = window->children().begin(); - it != window->children().end(); - ++it) - AcquireAllLayers(*it); - } - - void AcquireAllViewLayers(views::View* view) { - for (int i = 0; i < view->child_count(); ++i) - AcquireAllViewLayers(view->child_at(i)); - if (view->layer()) { - ui::Layer* layer = view->RecreateLayer(); - if (layer) { - layer->SuppressPaint(); - layers_.push_back(layer); - } - } - } - - aura::Window* window_; - std::vector<ui::Layer*> layers_; - - DISALLOW_COPY_AND_ASSIGN(HidingWindowAnimationObserver); -}; - -void GetTransformRelativeToRoot(ui::Layer* layer, gfx::Transform* transform) { - const Layer* root = layer; - while (root->parent()) - root = root->parent(); - layer->GetTargetTransformRelativeTo(root, transform); -} - -gfx::Rect GetLayerWorldBoundsAfterTransform(ui::Layer* layer, - const gfx::Transform& transform) { - gfx::Transform in_world = transform; - GetTransformRelativeToRoot(layer, &in_world); - - gfx::RectF transformed = layer->bounds(); - in_world.TransformRect(&transformed); - - return gfx::ToEnclosingRect(transformed); -} - -// Augment the host window so that the enclosing bounds of the full -// animation will fit inside of it. -void AugmentWindowSize(aura::Window* window, - const gfx::Transform& end_transform) { - aura::client::AnimationHost* animation_host = - aura::client::GetAnimationHost(window); - if (!animation_host) - return; - - const gfx::Rect& world_at_start = window->bounds(); - gfx::Rect world_at_end = - GetLayerWorldBoundsAfterTransform(window->layer(), end_transform); - gfx::Rect union_in_window_space = - gfx::UnionRects(world_at_start, world_at_end); - - // Calculate the top left and bottom right deltas to be added to the window - // bounds. - gfx::Vector2d top_left_delta(world_at_start.x() - union_in_window_space.x(), - world_at_start.y() - union_in_window_space.y()); - - gfx::Vector2d bottom_right_delta( - union_in_window_space.x() + union_in_window_space.width() - - (world_at_start.x() + world_at_start.width()), - union_in_window_space.y() + union_in_window_space.height() - - (world_at_start.y() + world_at_start.height())); - - DCHECK(top_left_delta.x() >= 0 && top_left_delta.y() >= 0 && - bottom_right_delta.x() >= 0 && bottom_right_delta.y() >= 0); - - animation_host->SetHostTransitionOffsets(top_left_delta, bottom_right_delta); -} - -// Shows a window using an animation, animating its opacity from 0.f to 1.f, -// its visibility to true, and its transform from |start_transform| to -// |end_transform|. -void AnimateShowWindowCommon(aura::Window* window, - const gfx::Transform& start_transform, - const gfx::Transform& end_transform) { - window->layer()->set_delegate(window); - - AugmentWindowSize(window, end_transform); - - window->layer()->SetOpacity(kWindowAnimation_HideOpacity); - window->layer()->SetTransform(start_transform); - window->layer()->SetVisible(true); - - { - // Property sets within this scope will be implicitly animated. - ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator()); - base::TimeDelta duration = GetWindowVisibilityAnimationDuration(window); - if (duration.ToInternalValue() > 0) - settings.SetTransitionDuration(duration); - - window->layer()->SetTransform(end_transform); - window->layer()->SetOpacity(kWindowAnimation_ShowOpacity); - } -} - -// Hides a window using an animation, animating its opacity from 1.f to 0.f, -// its visibility to false, and its transform to |end_transform|. -void AnimateHideWindowCommon(aura::Window* window, - const gfx::Transform& end_transform) { - AugmentWindowSize(window, end_transform); - window->layer()->set_delegate(NULL); - - // Property sets within this scope will be implicitly animated. - ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator()); - settings.AddObserver(new HidingWindowAnimationObserver(window)); - - base::TimeDelta duration = GetWindowVisibilityAnimationDuration(window); - if (duration.ToInternalValue() > 0) - settings.SetTransitionDuration(duration); - - window->layer()->SetOpacity(kWindowAnimation_HideOpacity); - window->layer()->SetTransform(end_transform); - window->layer()->SetVisible(false); -} - -static gfx::Transform GetScaleForWindow(aura::Window* window) { - gfx::Rect bounds = window->bounds(); - gfx::Transform scale = gfx::GetScaleTransform( - gfx::Point(kWindowAnimation_TranslateFactor * bounds.width(), - kWindowAnimation_TranslateFactor * bounds.height()), - kWindowAnimation_ScaleFactor); - return scale; -} - -// Show/Hide windows using a shrink animation. -void AnimateShowWindow_Drop(aura::Window* window) { - AnimateShowWindowCommon(window, GetScaleForWindow(window), gfx::Transform()); -} - -void AnimateHideWindow_Drop(aura::Window* window) { - AnimateHideWindowCommon(window, GetScaleForWindow(window)); -} - -// Show/Hide windows using a vertical Glenimation. -void AnimateShowWindow_Vertical(aura::Window* window) { - gfx::Transform transform; - transform.Translate(0, window->GetProperty( - kWindowVisibilityAnimationVerticalPositionKey)); - AnimateShowWindowCommon(window, transform, gfx::Transform()); -} - -void AnimateHideWindow_Vertical(aura::Window* window) { - gfx::Transform transform; - transform.Translate(0, window->GetProperty( - kWindowVisibilityAnimationVerticalPositionKey)); - AnimateHideWindowCommon(window, transform); -} - -// Show/Hide windows using a fade. -void AnimateShowWindow_Fade(aura::Window* window) { - AnimateShowWindowCommon(window, gfx::Transform(), gfx::Transform()); -} - -void AnimateHideWindow_Fade(aura::Window* window) { - AnimateHideWindowCommon(window, gfx::Transform()); -} - -ui::LayerAnimationElement* CreateGrowShrinkElement( - aura::Window* window, bool grow) { - scoped_ptr<ui::InterpolatedTransform> scale(new ui::InterpolatedScale( - gfx::Point3F(kWindowAnimation_Bounce_Scale, - kWindowAnimation_Bounce_Scale, - 1), - gfx::Point3F(1, 1, 1))); - scoped_ptr<ui::InterpolatedTransform> scale_about_pivot( - new ui::InterpolatedTransformAboutPivot( - gfx::Point(window->bounds().width() * 0.5, - window->bounds().height() * 0.5), - scale.release())); - scale_about_pivot->SetReversed(grow); - scoped_ptr<ui::LayerAnimationElement> transition( - ui::LayerAnimationElement::CreateInterpolatedTransformElement( - scale_about_pivot.release(), - base::TimeDelta::FromMilliseconds( - kWindowAnimation_Bounce_DurationMS * - kWindowAnimation_Bounce_GrowShrinkDurationPercent / 100))); - transition->set_tween_type(grow ? gfx::Tween::EASE_OUT : gfx::Tween::EASE_IN); - return transition.release(); -} - -void AnimateBounce(aura::Window* window) { - ui::ScopedLayerAnimationSettings scoped_settings( - window->layer()->GetAnimator()); - scoped_settings.SetPreemptionStrategy( - ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS); - window->layer()->set_delegate(window); - scoped_ptr<ui::LayerAnimationSequence> sequence( - new ui::LayerAnimationSequence); - sequence->AddElement(CreateGrowShrinkElement(window, true)); - ui::LayerAnimationElement::AnimatableProperties paused_properties; - paused_properties.insert(ui::LayerAnimationElement::BOUNDS); - sequence->AddElement(ui::LayerAnimationElement::CreatePauseElement( - paused_properties, - base::TimeDelta::FromMilliseconds( - kWindowAnimation_Bounce_DurationMS * - (100 - 2 * kWindowAnimation_Bounce_GrowShrinkDurationPercent) / - 100))); - sequence->AddElement(CreateGrowShrinkElement(window, false)); - window->layer()->GetAnimator()->StartAnimation(sequence.release()); -} - -void AddLayerAnimationsForRotate(aura::Window* window, bool show) { - window->layer()->set_delegate(window); - if (show) - window->layer()->SetOpacity(kWindowAnimation_HideOpacity); - - base::TimeDelta duration = base::TimeDelta::FromMilliseconds( - kWindowAnimation_Rotate_DurationMS); - - if (!show) { - new HidingWindowAnimationObserver(window); - window->layer()->GetAnimator()->SchedulePauseForProperties( - duration * (100 - kWindowAnimation_Rotate_OpacityDurationPercent) / 100, - ui::LayerAnimationElement::OPACITY, - -1); - } - scoped_ptr<ui::LayerAnimationElement> opacity( - ui::LayerAnimationElement::CreateOpacityElement( - show ? kWindowAnimation_ShowOpacity : kWindowAnimation_HideOpacity, - duration * kWindowAnimation_Rotate_OpacityDurationPercent / 100)); - opacity->set_tween_type(gfx::Tween::EASE_IN_OUT); - window->layer()->GetAnimator()->ScheduleAnimation( - new ui::LayerAnimationSequence(opacity.release())); - - float xcenter = window->bounds().width() * 0.5; - - gfx::Transform transform; - transform.Translate(xcenter, 0); - transform.ApplyPerspectiveDepth(kWindowAnimation_Rotate_PerspectiveDepth); - transform.Translate(-xcenter, 0); - scoped_ptr<ui::InterpolatedTransform> perspective( - new ui::InterpolatedConstantTransform(transform)); - - scoped_ptr<ui::InterpolatedTransform> scale( - new ui::InterpolatedScale(1, kWindowAnimation_Rotate_ScaleFactor)); - scoped_ptr<ui::InterpolatedTransform> scale_about_pivot( - new ui::InterpolatedTransformAboutPivot( - gfx::Point(xcenter, kWindowAnimation_Rotate_TranslateY), - scale.release())); - - scoped_ptr<ui::InterpolatedTransform> translation( - new ui::InterpolatedTranslation(gfx::Point(), gfx::Point( - 0, kWindowAnimation_Rotate_TranslateY))); - - scoped_ptr<ui::InterpolatedTransform> rotation( - new ui::InterpolatedAxisAngleRotation( - gfx::Vector3dF(1, 0, 0), 0, kWindowAnimation_Rotate_DegreesX)); - - scale_about_pivot->SetChild(perspective.release()); - translation->SetChild(scale_about_pivot.release()); - rotation->SetChild(translation.release()); - rotation->SetReversed(show); - - scoped_ptr<ui::LayerAnimationElement> transition( - ui::LayerAnimationElement::CreateInterpolatedTransformElement( - rotation.release(), duration)); - - window->layer()->GetAnimator()->ScheduleAnimation( - new ui::LayerAnimationSequence(transition.release())); -} - -void AnimateShowWindow_Rotate(aura::Window* window) { - AddLayerAnimationsForRotate(window, true); -} - -void AnimateHideWindow_Rotate(aura::Window* window) { - AddLayerAnimationsForRotate(window, false); -} - -bool AnimateShowWindow(aura::Window* window) { - if (!HasWindowVisibilityAnimationTransition(window, ANIMATE_SHOW)) { - if (HasWindowVisibilityAnimationTransition(window, ANIMATE_HIDE)) { - // Since hide animation may have changed opacity and transform, - // reset them to show the window. - window->layer()->set_delegate(window); - window->layer()->SetOpacity(kWindowAnimation_ShowOpacity); - window->layer()->SetTransform(gfx::Transform()); - } - return false; - } - - switch (GetWindowVisibilityAnimationType(window)) { - case WINDOW_VISIBILITY_ANIMATION_TYPE_DROP: - AnimateShowWindow_Drop(window); - return true; - case WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL: - AnimateShowWindow_Vertical(window); - return true; - case WINDOW_VISIBILITY_ANIMATION_TYPE_FADE: - AnimateShowWindow_Fade(window); - return true; - case WINDOW_VISIBILITY_ANIMATION_TYPE_ROTATE: - AnimateShowWindow_Rotate(window); - return true; - default: - return false; - } -} - -bool AnimateHideWindow(aura::Window* window) { - if (!HasWindowVisibilityAnimationTransition(window, ANIMATE_HIDE)) { - if (HasWindowVisibilityAnimationTransition(window, ANIMATE_SHOW)) { - // Since show animation may have changed opacity and transform, - // reset them, though the change should be hidden. - window->layer()->set_delegate(NULL); - window->layer()->SetOpacity(kWindowAnimation_HideOpacity); - window->layer()->SetTransform(gfx::Transform()); - } - return false; - } - - switch (GetWindowVisibilityAnimationType(window)) { - case WINDOW_VISIBILITY_ANIMATION_TYPE_DROP: - AnimateHideWindow_Drop(window); - return true; - case WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL: - AnimateHideWindow_Vertical(window); - return true; - case WINDOW_VISIBILITY_ANIMATION_TYPE_FADE: - AnimateHideWindow_Fade(window); - return true; - case WINDOW_VISIBILITY_ANIMATION_TYPE_ROTATE: - AnimateHideWindow_Rotate(window); - return true; - default: - return false; - } -} - -} // namespace - -//////////////////////////////////////////////////////////////////////////////// -// External interface - -void SetWindowVisibilityAnimationType(aura::Window* window, int type) { - window->SetProperty(kWindowVisibilityAnimationTypeKey, type); -} - -int GetWindowVisibilityAnimationType(aura::Window* window) { - return window->GetProperty(kWindowVisibilityAnimationTypeKey); -} - -void SetWindowVisibilityAnimationTransition( - aura::Window* window, - WindowVisibilityAnimationTransition transition) { - window->SetProperty(kWindowVisibilityAnimationTransitionKey, transition); -} - -bool HasWindowVisibilityAnimationTransition( - aura::Window* window, - WindowVisibilityAnimationTransition transition) { - WindowVisibilityAnimationTransition prop = window->GetProperty( - kWindowVisibilityAnimationTransitionKey); - return (prop & transition) != 0; -} - -void SetWindowVisibilityAnimationDuration(aura::Window* window, - const TimeDelta& duration) { - window->SetProperty(kWindowVisibilityAnimationDurationKey, - static_cast<int>(duration.ToInternalValue())); -} - -void SetWindowVisibilityAnimationVerticalPosition(aura::Window* window, - float position) { - window->SetProperty(kWindowVisibilityAnimationVerticalPositionKey, position); -} - -ui::ImplicitAnimationObserver* CreateHidingWindowAnimationObserver( - aura::Window* window) { - return new HidingWindowAnimationObserver(window); -} - -bool AnimateOnChildWindowVisibilityChanged(aura::Window* window, bool visible) { - if (WindowAnimationsDisabled(window)) - return false; - if (visible) - return AnimateShowWindow(window); - // Don't start hiding the window again if it's already being hidden. - return window->layer()->GetTargetOpacity() != 0.0f && - AnimateHideWindow(window); -} - -bool AnimateWindow(aura::Window* window, WindowAnimationType type) { - switch (type) { - case WINDOW_ANIMATION_TYPE_BOUNCE: - AnimateBounce(window); - return true; - default: - NOTREACHED(); - return false; - } -} - -bool WindowAnimationsDisabled(aura::Window* window) { - return (window && - window->GetProperty(aura::client::kAnimationsDisabledKey)) || - CommandLine::ForCurrentProcess()->HasSwitch( - switches::kWindowAnimationsDisabled); -} - -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/window_animations.h b/chromium/ui/views/corewm/window_animations.h deleted file mode 100644 index 996c215dc89..00000000000 --- a/chromium/ui/views/corewm/window_animations.h +++ /dev/null @@ -1,101 +0,0 @@ -// 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_COREWM_WINDOW_ANIMATIONS_H_ -#define UI_VIEWS_COREWM_WINDOW_ANIMATIONS_H_ - -#include <vector> - -#include "ui/views/views_export.h" - -namespace aura { -class Window; -} -namespace base { -class TimeDelta; -} -namespace gfx { -class Rect; -} -namespace ui { -class ImplicitAnimationObserver; -class Layer; -class LayerAnimationSequence; -} - -namespace views { -namespace corewm { - -// A variety of canned animations for window transitions. -enum WindowVisibilityAnimationType { - WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT = 0, // Default. Lets the system - // decide based on window - // type. - WINDOW_VISIBILITY_ANIMATION_TYPE_DROP, // Window shrinks in. - WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL, // Vertical Glenimation. - WINDOW_VISIBILITY_ANIMATION_TYPE_FADE, // Fades in/out. - WINDOW_VISIBILITY_ANIMATION_TYPE_ROTATE, // Window rotates in. - - // Downstream library animations start above this point. - WINDOW_VISIBILITY_ANIMATION_MAX -}; - -// Canned animations that take effect once but don't have a symmetric pair as -// visibility animations do. -enum WindowAnimationType { - WINDOW_ANIMATION_TYPE_BOUNCE = 0, // Window scales up and down. -}; - -// Type of visibility change transition that a window should animate. -// Default behavior is to animate both show and hide. -enum WindowVisibilityAnimationTransition { - ANIMATE_SHOW = 0x1, - ANIMATE_HIDE = 0x2, - ANIMATE_BOTH = ANIMATE_SHOW | ANIMATE_HIDE, - ANIMATE_NONE = 0x4, -}; - -// These two methods use int for type rather than WindowVisibilityAnimationType -// since downstream libraries can extend the set of animations. -VIEWS_EXPORT void SetWindowVisibilityAnimationType(aura::Window* window, - int type); -VIEWS_EXPORT int GetWindowVisibilityAnimationType(aura::Window* window); - -VIEWS_EXPORT void SetWindowVisibilityAnimationTransition( - aura::Window* window, - WindowVisibilityAnimationTransition transition); - -VIEWS_EXPORT bool HasWindowVisibilityAnimationTransition( - aura::Window* window, - WindowVisibilityAnimationTransition transition); - -VIEWS_EXPORT void SetWindowVisibilityAnimationDuration( - aura::Window* window, - const base::TimeDelta& duration); - -VIEWS_EXPORT void SetWindowVisibilityAnimationVerticalPosition( - aura::Window* window, - float position); - -// Creates an ImplicitAnimationObserver that takes ownership of the layers -// associated with a Window so that the animation can continue after the Window -// has been destroyed. -// The returned object deletes itself when the animations are done. -VIEWS_EXPORT ui::ImplicitAnimationObserver* CreateHidingWindowAnimationObserver( - aura::Window* window); - -// Returns false if the |window| didn't animate. -VIEWS_EXPORT bool AnimateOnChildWindowVisibilityChanged(aura::Window* window, - bool visible); -VIEWS_EXPORT bool AnimateWindow(aura::Window* window, WindowAnimationType type); - -// Returns true if window animations are disabled for |window|. Window -// animations are enabled by default. If |window| is NULL, this just checks -// if the global flag disabling window animations is present. -VIEWS_EXPORT bool WindowAnimationsDisabled(aura::Window* window); - -} // namespace corewm -} // namespace views - -#endif // UI_VIEWS_COREWM_WINDOW_ANIMATIONS_H_ diff --git a/chromium/ui/views/corewm/window_animations_unittest.cc b/chromium/ui/views/corewm/window_animations_unittest.cc deleted file mode 100644 index 89d48c6ce8d..00000000000 --- a/chromium/ui/views/corewm/window_animations_unittest.cc +++ /dev/null @@ -1,143 +0,0 @@ -// 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/corewm/window_animations.h" - -#include "base/time/time.h" -#include "ui/aura/client/animation_host.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/layer_animator.h" -#include "ui/compositor/scoped_animation_duration_scale_mode.h" -#include "ui/gfx/animation/animation_container_element.h" -#include "ui/gfx/vector2d.h" - -using aura::Window; -using ui::Layer; - -namespace views { -namespace corewm { - -class WindowAnimationsTest : public aura::test::AuraTestBase { - public: - WindowAnimationsTest() {} - - virtual void TearDown() OVERRIDE { - AuraTestBase::TearDown(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(WindowAnimationsTest); -}; - -TEST_F(WindowAnimationsTest, LayerTargetVisibility) { - scoped_ptr<aura::Window> window( - aura::test::CreateTestWindowWithId(0, NULL)); - - // Layer target visibility changes according to Show/Hide. - window->Show(); - EXPECT_TRUE(window->layer()->GetTargetVisibility()); - window->Hide(); - EXPECT_FALSE(window->layer()->GetTargetVisibility()); - window->Show(); - EXPECT_TRUE(window->layer()->GetTargetVisibility()); -} - -TEST_F(WindowAnimationsTest, LayerTargetVisibility_AnimateShow) { - // Tests if opacity and transform are reset when only show animation is - // enabled. See also LayerTargetVisibility_AnimateHide. - // Since the window is not visible after Hide() is called, opacity and - // transform shouldn't matter in case of ANIMATE_SHOW, but we reset them - // to keep consistency. - - scoped_ptr<aura::Window> window(aura::test::CreateTestWindowWithId(0, NULL)); - SetWindowVisibilityAnimationTransition(window.get(), ANIMATE_SHOW); - - // Layer target visibility and opacity change according to Show/Hide. - window->Show(); - AnimateOnChildWindowVisibilityChanged(window.get(), true); - EXPECT_TRUE(window->layer()->GetTargetVisibility()); - EXPECT_EQ(1, window->layer()->opacity()); - - window->Hide(); - AnimateOnChildWindowVisibilityChanged(window.get(), false); - EXPECT_FALSE(window->layer()->GetTargetVisibility()); - EXPECT_EQ(0, window->layer()->opacity()); - EXPECT_EQ(gfx::Transform(), window->layer()->transform()); - - window->Show(); - AnimateOnChildWindowVisibilityChanged(window.get(), true); - EXPECT_TRUE(window->layer()->GetTargetVisibility()); - EXPECT_EQ(1, window->layer()->opacity()); -} - -TEST_F(WindowAnimationsTest, LayerTargetVisibility_AnimateHide) { - // Tests if opacity and transform are reset when only hide animation is - // enabled. Hide animation changes opacity and transform in addition to - // visibility, so we need to reset not only visibility but also opacity - // and transform to show the window. - - scoped_ptr<aura::Window> window(aura::test::CreateTestWindowWithId(0, NULL)); - SetWindowVisibilityAnimationTransition(window.get(), ANIMATE_HIDE); - - // Layer target visibility and opacity change according to Show/Hide. - window->Show(); - AnimateOnChildWindowVisibilityChanged(window.get(), true); - EXPECT_TRUE(window->layer()->GetTargetVisibility()); - EXPECT_EQ(1, window->layer()->opacity()); - EXPECT_EQ(gfx::Transform(), window->layer()->transform()); - - window->Hide(); - AnimateOnChildWindowVisibilityChanged(window.get(), false); - EXPECT_FALSE(window->layer()->GetTargetVisibility()); - EXPECT_EQ(0, window->layer()->opacity()); - - window->Show(); - AnimateOnChildWindowVisibilityChanged(window.get(), true); - EXPECT_TRUE(window->layer()->GetTargetVisibility()); - EXPECT_EQ(1, window->layer()->opacity()); - EXPECT_EQ(gfx::Transform(), window->layer()->transform()); -} - -// A simple AnimationHost implementation for the NotifyHideCompleted test. -class NotifyHideCompletedAnimationHost : public aura::client::AnimationHost { - public: - NotifyHideCompletedAnimationHost() : hide_completed_(false) {} - virtual ~NotifyHideCompletedAnimationHost() {} - - // Overridden from TestWindowDelegate: - virtual void OnWindowHidingAnimationCompleted() OVERRIDE { - hide_completed_ = true; - } - - virtual void SetHostTransitionOffsets( - const gfx::Vector2d& top_left, - const gfx::Vector2d& bottom_right) OVERRIDE {} - - bool hide_completed() const { return hide_completed_; } - - private: - bool hide_completed_; - - DISALLOW_COPY_AND_ASSIGN(NotifyHideCompletedAnimationHost); -}; - -TEST_F(WindowAnimationsTest, NotifyHideCompleted) { - NotifyHideCompletedAnimationHost animation_host; - scoped_ptr<aura::Window> window(aura::test::CreateTestWindowWithId(0, NULL)); - aura::client::SetAnimationHost(window.get(), &animation_host); - views::corewm::SetWindowVisibilityAnimationType( - window.get(), WINDOW_VISIBILITY_ANIMATION_TYPE_FADE); - AnimateOnChildWindowVisibilityChanged(window.get(), true); - EXPECT_TRUE(window->layer()->visible()); - - EXPECT_FALSE(animation_host.hide_completed()); - AnimateOnChildWindowVisibilityChanged(window.get(), false); - EXPECT_TRUE(animation_host.hide_completed()); -} - -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/window_modality_controller.cc b/chromium/ui/views/corewm/window_modality_controller.cc deleted file mode 100644 index b52a9d6ad8c..00000000000 --- a/chromium/ui/views/corewm/window_modality_controller.cc +++ /dev/null @@ -1,196 +0,0 @@ -// 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/corewm/window_modality_controller.h" - -#include <algorithm> - -#include "ui/aura/client/aura_constants.h" -#include "ui/aura/client/capture_client.h" -#include "ui/aura/env.h" -#include "ui/aura/root_window.h" -#include "ui/aura/window.h" -#include "ui/aura/window_property.h" -#include "ui/base/ui_base_types.h" -#include "ui/events/event.h" -#include "ui/events/event_target.h" -#include "ui/events/gestures/gesture_recognizer.h" -#include "ui/views/corewm/window_animations.h" -#include "ui/views/corewm/window_util.h" - -namespace views { -namespace corewm { - -// Transient child's modal parent. -extern const aura::WindowProperty<aura::Window*>* const kModalParentKey; -DEFINE_WINDOW_PROPERTY_KEY(aura::Window*, kModalParentKey, NULL); - -namespace { - -bool HasAncestor(aura::Window* window, aura::Window* ancestor) { - if (!window) - return false; - if (window == ancestor) - return true; - return HasAncestor(window->parent(), ancestor); -} - -bool TransientChildIsWindowModal(aura::Window* window) { - return window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_WINDOW; -} - -bool TransientChildIsSystemModal(aura::Window* window) { - return window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_SYSTEM; -} - -bool TransientChildIsChildModal(aura::Window* window) { - return window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_CHILD; -} - -aura::Window* GetModalParent(aura::Window* window) { - return window->GetProperty(kModalParentKey); -} - -bool IsModalTransientChild(aura::Window* transient, aura::Window* original) { - return transient->IsVisible() && - (TransientChildIsWindowModal(transient) || - TransientChildIsSystemModal(transient) || - (TransientChildIsChildModal(transient) && - (HasAncestor(original, GetModalParent(transient))))); -} - -aura::Window* GetModalTransientChild( - aura::Window* activatable, - aura::Window* original) { - aura::Window::Windows::const_iterator it; - for (it = activatable->transient_children().begin(); - it != activatable->transient_children().end(); - ++it) { - aura::Window* transient = *it; - if (IsModalTransientChild(transient, original)) { - return transient->transient_children().empty() ? - transient : GetModalTransientChild(transient, original); - } - } - return NULL; -} - -} // namespace - -void SetModalParent(aura::Window* child, aura::Window* parent) { - child->SetProperty(kModalParentKey, parent); -} - -aura::Window* GetModalTransient(aura::Window* window) { - if (!window) - return NULL; - - // We always want to check the for the transient child of the toplevel window. - aura::Window* toplevel = GetToplevelWindow(window); - if (!toplevel) - return NULL; - - return GetModalTransientChild(toplevel, window); -} - -//////////////////////////////////////////////////////////////////////////////// -// WindowModalityController, public: - -WindowModalityController::WindowModalityController( - ui::EventTarget* event_target) - : event_target_(event_target) { - aura::Env::GetInstance()->AddObserver(this); - DCHECK(event_target->IsPreTargetListEmpty()); - event_target_->AddPreTargetHandler(this); -} - -WindowModalityController::~WindowModalityController() { - event_target_->RemovePreTargetHandler(this); - aura::Env::GetInstance()->RemoveObserver(this); - for (size_t i = 0; i < windows_.size(); ++i) - windows_[i]->RemoveObserver(this); -} - -//////////////////////////////////////////////////////////////////////////////// -// WindowModalityController, aura::EventFilter implementation: - -void WindowModalityController::OnKeyEvent(ui::KeyEvent* event) { - aura::Window* target = static_cast<aura::Window*>(event->target()); - if (GetModalTransient(target)) - event->SetHandled(); -} - -void WindowModalityController::OnMouseEvent(ui::MouseEvent* event) { - aura::Window* target = static_cast<aura::Window*>(event->target()); - if (ProcessLocatedEvent(target, event)) - event->SetHandled(); -} - -void WindowModalityController::OnTouchEvent(ui::TouchEvent* event) { - aura::Window* target = static_cast<aura::Window*>(event->target()); - if (ProcessLocatedEvent(target, event)) - event->SetHandled(); -} - -//////////////////////////////////////////////////////////////////////////////// -// WindowModalityController, aura::EnvObserver implementation: - -void WindowModalityController::OnWindowInitialized(aura::Window* window) { - windows_.push_back(window); - window->AddObserver(this); -} - -//////////////////////////////////////////////////////////////////////////////// -// WindowModalityController, aura::WindowObserver implementation: - -void WindowModalityController::OnWindowPropertyChanged(aura::Window* window, - const void* key, - intptr_t old) { - // In tests, we sometimes create the modality relationship after a window is - // visible. - if (key == aura::client::kModalKey && - window->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_NONE && - window->IsVisible()) { - ActivateWindow(window); - ui::GestureRecognizer::Get()->TransferEventsTo( - window->transient_parent(), NULL); - } -} - -void WindowModalityController::OnWindowVisibilityChanged( - aura::Window* window, - bool visible) { - if (visible && window->GetProperty(aura::client::kModalKey) != - ui::MODAL_TYPE_NONE) { - ui::GestureRecognizer::Get()->TransferEventsTo( - window->transient_parent(), NULL); - // Make sure no other window has capture, otherwise |window| won't get mouse - // events. - aura::Window* capture_window = aura::client::GetCaptureWindow(window); - if (capture_window) - capture_window->ReleaseCapture(); - } -} - -void WindowModalityController::OnWindowDestroyed(aura::Window* window) { - windows_.erase(std::find(windows_.begin(), windows_.end(), window)); - window->RemoveObserver(this); -} - -bool WindowModalityController::ProcessLocatedEvent(aura::Window* target, - ui::LocatedEvent* event) { - if (event->handled()) - return false; - aura::Window* modal_transient_child = GetModalTransient(target); - if (modal_transient_child && (event->type() == ui::ET_MOUSE_PRESSED || - event->type() == ui::ET_TOUCH_PRESSED)) { - AnimateWindow(modal_transient_child, WINDOW_ANIMATION_TYPE_BOUNCE); - } - if (event->type() == ui::ET_TOUCH_CANCELLED) - return false; - return !!modal_transient_child; -} - -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/window_modality_controller.h b/chromium/ui/views/corewm/window_modality_controller.h deleted file mode 100644 index 88150a54253..00000000000 --- a/chromium/ui/views/corewm/window_modality_controller.h +++ /dev/null @@ -1,73 +0,0 @@ -// 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_COREWM_WINDOW_MODALITY_CONTROLLER_H_ -#define UI_VIEWS_COREWM_WINDOW_MODALITY_CONTROLLER_H_ - -#include <vector> - -#include "base/compiler_specific.h" -#include "ui/aura/env_observer.h" -#include "ui/aura/window_observer.h" -#include "ui/events/event_handler.h" -#include "ui/views/views_export.h" - -namespace ui { -class EventTarget; -class LocatedEvent; -} - -namespace views { -namespace corewm { - -// Sets the modal parent for the child. -VIEWS_EXPORT void SetModalParent(aura::Window* child, aura::Window* parent); - -// Returns the modal transient child of |window|, or NULL if |window| does not -// have any modal transient children. -VIEWS_EXPORT aura::Window* GetModalTransient(aura::Window* window); - -// WindowModalityController is an event filter that consumes events sent to -// windows that are the transient parents of window-modal windows. This filter -// must be added to the CompoundEventFilter so that activation works properly. -class VIEWS_EXPORT WindowModalityController : public ui::EventHandler, - public aura::EnvObserver, - public aura::WindowObserver { - public: - explicit WindowModalityController(ui::EventTarget* event_target); - virtual ~WindowModalityController(); - - // Overridden from ui::EventHandler: - virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE; - virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE; - virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE; - - // Overridden from aura::EnvObserver: - virtual void OnWindowInitialized(aura::Window* window) OVERRIDE; - - // Overridden from aura::WindowObserver: - virtual void OnWindowPropertyChanged(aura::Window* window, - const void* key, - intptr_t old) OVERRIDE; - virtual void OnWindowVisibilityChanged(aura::Window* window, - bool visible) OVERRIDE; - virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE; - - private: - // Processes a mouse/touch event, and returns true if the event should be - // consumed. - bool ProcessLocatedEvent(aura::Window* target, - ui::LocatedEvent* event); - - std::vector<aura::Window*> windows_; - - ui::EventTarget* event_target_; - - DISALLOW_COPY_AND_ASSIGN(WindowModalityController); -}; - -} // namespace corewm -} // namespace views - -#endif // UI_VIEWS_COREWM_WINDOW_MODALITY_CONTROLLER_H_ diff --git a/chromium/ui/views/corewm/window_util.cc b/chromium/ui/views/corewm/window_util.cc deleted file mode 100644 index eaee174d738..00000000000 --- a/chromium/ui/views/corewm/window_util.cc +++ /dev/null @@ -1,141 +0,0 @@ -// 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/corewm/window_util.h" - -#include "ui/aura/client/activation_client.h" -#include "ui/aura/root_window.h" -#include "ui/aura/window.h" -#include "ui/compositor/layer.h" -#include "ui/views/view.h" -#include "ui/views/widget/widget.h" - -namespace { - -// Helper method for RecreateWindowLayers() which adds all the existing layers -// for |view| and its descendants to |parent_layer|. New layers are created for -// |view| (if it previously had a layer) and any descendants which previously -// had layers. The new layers are blank, so nothing has been painted to them -// yet. Returns true if this method added at least one layer to |parent_layer|. -bool RecreateViewLayers(ui::Layer* parent_layer, views::View* view) { - bool recreated_layer = false; - if (view->layer()) { - ui::Layer* layer = view->RecreateLayer(); - if (layer) { - layer->SuppressPaint(); - parent_layer->Add(layer); - parent_layer = layer; - recreated_layer = true; - } - } - for (int i = 0; i < view->child_count(); ++i) - recreated_layer |= RecreateViewLayers(parent_layer, view->child_at(i)); - - return recreated_layer; -} - -} // namespace - -namespace views { -namespace corewm { - -void ActivateWindow(aura::Window* window) { - DCHECK(window); - DCHECK(window->GetRootWindow()); - aura::client::GetActivationClient(window->GetRootWindow())->ActivateWindow( - window); -} - -void DeactivateWindow(aura::Window* window) { - DCHECK(window); - DCHECK(window->GetRootWindow()); - aura::client::GetActivationClient(window->GetRootWindow())->DeactivateWindow( - window); -} - -bool IsActiveWindow(aura::Window* window) { - DCHECK(window); - if (!window->GetRootWindow()) - return false; - aura::client::ActivationClient* client = - aura::client::GetActivationClient(window->GetRootWindow()); - return client && client->GetActiveWindow() == window; -} - -bool CanActivateWindow(aura::Window* window) { - DCHECK(window); - if (!window->GetRootWindow()) - return false; - aura::client::ActivationClient* client = - aura::client::GetActivationClient(window->GetRootWindow()); - return client && client->CanActivateWindow(window); -} - -aura::Window* GetActivatableWindow(aura::Window* window) { - aura::client::ActivationClient* client = - aura::client::GetActivationClient(window->GetRootWindow()); - return client ? client->GetActivatableWindow(window) : NULL; -} - -aura::Window* GetToplevelWindow(aura::Window* window) { - aura::client::ActivationClient* client = - aura::client::GetActivationClient(window->GetRootWindow()); - return client ? client->GetToplevelWindow(window) : NULL; -} - -void DeepDeleteLayers(ui::Layer* layer) { - std::vector<ui::Layer*> children = layer->children(); - for (std::vector<ui::Layer*>::const_iterator it = children.begin(); - it != children.end(); - ++it) { - ui::Layer* child = *it; - DeepDeleteLayers(child); - } - delete layer; -} - -ui::Layer* RecreateWindowLayers(aura::Window* window, bool set_bounds) { - const gfx::Rect bounds = window->bounds(); - ui::Layer* old_layer = window->RecreateLayer(); - DCHECK(old_layer); - - // Cache the order of |window|'s child layers. If |window| belongs to a widget - // and the widget has both child windows and child views with layers, - // |initial_layer_order| is used to determine the relative order. - std::vector<ui::Layer*> initial_layer_order = window->layer()->children(); - - // Recreate the layers for any child windows of |window|. - for (aura::Window::Windows::const_iterator it = window->children().begin(); - it != window->children().end(); - ++it) { - old_layer->Add(RecreateWindowLayers(*it, set_bounds)); - } - - // Recreate the layers for any child views of |widget|. - bool has_view_layers = false; - views::Widget* widget = views::Widget::GetWidgetForNativeView(window); - if (widget && widget->GetRootView()) - has_view_layers = RecreateViewLayers(old_layer, widget->GetRootView()); - - if (has_view_layers && !window->children().empty()) { - // RecreateViewLayers() added the view layers above the window layers in - // z-order. The window layers and the view layers may have been originally - // intermingled. Reorder |old_layer|'s children based on the initial - // order. - for (size_t i = 0; i < initial_layer_order.size(); ++i) { - ui::Layer* layer = initial_layer_order[i]; - std::vector<ui::Layer*>::const_iterator it = std::find( - old_layer->children().begin(), old_layer->children().end(), layer); - if (it != old_layer->children().end()) - old_layer->StackAtTop(layer); - } - } - - if (set_bounds) - window->SetBounds(bounds); - return old_layer; -} - -} // namespace corewm -} // namespace views diff --git a/chromium/ui/views/corewm/window_util.h b/chromium/ui/views/corewm/window_util.h deleted file mode 100644 index 01a3ea43f06..00000000000 --- a/chromium/ui/views/corewm/window_util.h +++ /dev/null @@ -1,53 +0,0 @@ -// 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_COREWM_WINDOW_UTIL_H_ -#define UI_VIEWS_COREWM_WINDOW_UTIL_H_ - -#include "base/compiler_specific.h" -#include "ui/views/views_export.h" - -namespace aura { -class Window; -} - -namespace ui { -class Layer; -} - -namespace views { -namespace corewm { - -VIEWS_EXPORT void ActivateWindow(aura::Window* window); -VIEWS_EXPORT void DeactivateWindow(aura::Window* window); -VIEWS_EXPORT bool IsActiveWindow(aura::Window* window); -VIEWS_EXPORT bool CanActivateWindow(aura::Window* window); - -// Retrieves the activatable window for |window|. The ActivationClient makes -// this determination. -VIEWS_EXPORT aura::Window* GetActivatableWindow(aura::Window* window); - -// Retrieves the toplevel window for |window|. The ActivationClient makes this -// determination. -VIEWS_EXPORT aura::Window* GetToplevelWindow(aura::Window* window); - -// Deletes |layer| and all its child layers. -VIEWS_EXPORT void DeepDeleteLayers(ui::Layer* layer); - -// Returns the existing Layer for |window| (and all its descendants) and creates -// a new layer for |window| and all its descendants. This is intended for -// animations that want to animate between the existing visuals and a new window -// state. The caller owns the return value. -// -// As a result of this |window| has freshly created layers, meaning the layers -// are all empty (nothing has been painted to them) and are sized to 0x0. Soon -// after this call you need to reset the bounds of the window. Or, you can pass -// true as the second argument to let the function do that. -VIEWS_EXPORT ui::Layer* RecreateWindowLayers(aura::Window* window, - bool set_bounds) WARN_UNUSED_RESULT; - -} // namespace corewm -} // namespace views - -#endif // UI_VIEWS_COREWM_WINDOW_UTIL_H_ diff --git a/chromium/ui/views/corewm/window_util_unittest.cc b/chromium/ui/views/corewm/window_util_unittest.cc deleted file mode 100644 index fdf3a46fa2f..00000000000 --- a/chromium/ui/views/corewm/window_util_unittest.cc +++ /dev/null @@ -1,117 +0,0 @@ -// 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/corewm/window_util.h" - -#include "ui/aura/root_window.h" -#include "ui/aura/test/aura_test_base.h" -#include "ui/aura/window.h" -#include "ui/compositor/test/test_layers.h" -#include "ui/views/view_constants_aura.h" -#include "ui/views/widget/widget.h" - -class WindowUtilTest : public aura::test::AuraTestBase { - public: - WindowUtilTest() { - } - - virtual ~WindowUtilTest() { - } - - // Creates a widget of TYPE_CONTROL. - // The caller takes ownership of the returned widget. - views::Widget* CreateControlWidget( - aura::Window* parent, - const gfx::Rect& bounds) const WARN_UNUSED_RESULT { - views::Widget::InitParams params(views::Widget::InitParams::TYPE_CONTROL); - params.parent = parent; - params.bounds = bounds; - views::Widget* widget = new views::Widget(); - widget->Init(params); - return widget; - } - - // Returns a view with a layer with the passed in |bounds| and |layer_name|. - // The caller takes ownership of the returned view. - views::View* CreateViewWithLayer( - const gfx::Rect& bounds, - const char* layer_name) const WARN_UNUSED_RESULT { - views::View* view = new views::View(); - view->SetBoundsRect(bounds); - view->SetPaintToLayer(true); - view->layer()->set_name(layer_name); - return view; - } - - private: - DISALLOW_COPY_AND_ASSIGN(WindowUtilTest); -}; - -// Test that RecreateWindowLayers() recreates the layers for all child windows -// and all child views and that the z-order of the recreated layers matches that -// of the original layers. -// Test hierarchy: -// w1 -// +-- v1 -// +-- v2 (no layer) -// +-- v3 (no layer) -// +-- v4 -// +-- w2 -// +-- v5 -// +-- v6 -// +-- v7 -TEST_F(WindowUtilTest, RecreateWindowLayers) { - views::Widget* w1 = CreateControlWidget(root_window(), - gfx::Rect(0, 0, 100, 100)); - w1->GetNativeView()->layer()->set_name("w1"); - - views::View* v2 = new views::View(); - v2->SetBounds(0, 1, 100, 101); - views::View* v3 = new views::View(); - v3->SetBounds(0, 2, 100, 102); - views::View* w2_host_view = new views::View(); - - w1->GetRootView()->AddChildView(CreateViewWithLayer( - gfx::Rect(0, 3, 100, 103), "v1")); - w1->GetRootView()->AddChildView(v2); - v2->AddChildView(v3); - v2->AddChildView(CreateViewWithLayer(gfx::Rect(0, 4, 100, 104), "v4")); - - w1->GetRootView()->AddChildView(w2_host_view); - w1->GetRootView()->AddChildView(CreateViewWithLayer( - gfx::Rect(0, 4, 100, 104), "v7")); - - views::Widget* w2 = CreateControlWidget(w1->GetNativeView(), - gfx::Rect(0, 5, 100, 105)); - w2->GetNativeView()->layer()->set_name("w2"); - w2->GetNativeView()->SetProperty(views::kHostViewKey, w2_host_view); - - views::View* v5 = CreateViewWithLayer(gfx::Rect(0, 6, 100, 106), "v5"); - w2->GetRootView()->AddChildView(v5); - v5->AddChildView(CreateViewWithLayer(gfx::Rect(0, 7, 100, 107), "v6")); - - // Test the initial order of the layers. - ui::Layer* w1_layer = w1->GetNativeView()->layer(); - ASSERT_EQ("w1", w1_layer->name()); - ASSERT_EQ("v1 v4 w2 v7", ui::test::ChildLayerNamesAsString(*w1_layer)); - ui::Layer* w2_layer = w1_layer->children()[2]; - ASSERT_EQ("v5", ui::test::ChildLayerNamesAsString(*w2_layer)); - ui::Layer* v5_layer = w2_layer->children()[0]; - ASSERT_EQ("v6", ui::test::ChildLayerNamesAsString(*v5_layer)); - - w1_layer = views::corewm::RecreateWindowLayers(w1->GetNativeView(), false); - - // The order of the layers returned by RecreateWindowLayers() should match the - // order of the layers prior to calling RecreateWindowLayers(). - ASSERT_EQ("w1", w1_layer->name()); - ASSERT_EQ("v1 v4 w2 v7", ui::test::ChildLayerNamesAsString(*w1_layer)); - w2_layer = w1_layer->children()[2]; - ASSERT_EQ("v5", ui::test::ChildLayerNamesAsString(*w2_layer)); - v5_layer = w2_layer->children()[0]; - ASSERT_EQ("v6", ui::test::ChildLayerNamesAsString(*v5_layer)); - - views::corewm::DeepDeleteLayers(w1_layer); - // The views and the widgets are destroyed when AuraTestHelper::TearDown() - // destroys root_window(). -} diff --git a/chromium/ui/views/cull_set.cc b/chromium/ui/views/cull_set.cc new file mode 100644 index 00000000000..65566e0b362 --- /dev/null +++ b/chromium/ui/views/cull_set.cc @@ -0,0 +1,26 @@ +// Copyright 2014 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/cull_set.h" + +namespace views { + +CullSet::CullSet() { +} + +CullSet::~CullSet() { +} + +CullSet::CullSet(scoped_ptr<base::hash_set<intptr_t> > cull_set) + : cull_set_(cull_set.Pass()) { +} + +bool CullSet::ShouldPaint(const View* view) const { + if (cull_set_) + return (cull_set_->count(reinterpret_cast<intptr_t>(view)) > 0); + + return true; +} + +} // namespace views diff --git a/chromium/ui/views/cull_set.h b/chromium/ui/views/cull_set.h new file mode 100644 index 00000000000..03f7cff86ee --- /dev/null +++ b/chromium/ui/views/cull_set.h @@ -0,0 +1,47 @@ +// Copyright 2014 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_CULL_SET_H_ +#define UI_VIEWS_CULL_SET_H_ + +#include "base/containers/hash_tables.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "ui/views/views_export.h" + +namespace views { + +class View; + +// A CullSet defines a set of View pointers which have been possibly culled +// from painting or other bounds-checking operations. It wraps a set of +// pointers to views, or NULL if no such set is available. +class VIEWS_EXPORT CullSet { + public: + // Default constructor builds a CullSet that will always return true for + // ShouldPaint(). + CullSet(); + ~CullSet(); + + // Wraps a set of pointers to Views, as might be provided by + // gfx::RTree::Query(), that intersect the damage rect and therefore need + // to be painted. CullSet takes ownership of the provided pointer. + CullSet(scoped_ptr<base::hash_set<intptr_t> > cull_set); + + // Returns true if |view| needs to be painted. + bool ShouldPaint(const View* view) const; + + private: + friend class BoundsTreeTestView; + + // The set of Views that collided with the query rectangle provided to the + // RTree data structure, or NULL if one is not available. + scoped_ptr<base::hash_set<intptr_t> > cull_set_; + + DISALLOW_COPY_AND_ASSIGN(CullSet); +}; + +} // namespace views + +#endif // UI_VIEWS_CULL_SET_H_ diff --git a/chromium/ui/views/debug_utils.cc b/chromium/ui/views/debug_utils.cc index 536a6f6abf5..12b4759b33d 100644 --- a/chromium/ui/views/debug_utils.cc +++ b/chromium/ui/views/debug_utils.cc @@ -4,7 +4,7 @@ #include "ui/views/debug_utils.h" -#include <iostream> +#include <ostream> #include "base/logging.h" #include "base/strings/utf_string_conversions.h" @@ -18,7 +18,7 @@ void PrintViewHierarchyImp(const View* view, int ind = indent; while (ind-- > 0) *out << L' '; - *out << UTF8ToWide(view->GetClassName()); + *out << base::UTF8ToWide(view->GetClassName()); *out << L' '; *out << view->id(); *out << L' '; @@ -38,7 +38,7 @@ void PrintFocusHierarchyImp(const View* view, int ind = indent; while (ind-- > 0) *out << L' '; - *out << UTF8ToWide(view->GetClassName()); + *out << base::UTF8ToWide(view->GetClassName()); *out << L' '; *out << view->id(); *out << L' '; diff --git a/chromium/ui/views/drag_utils.cc b/chromium/ui/views/drag_utils.cc index 0b9c31775a2..0fd5e5e91c8 100644 --- a/chromium/ui/views/drag_utils.cc +++ b/chromium/ui/views/drag_utils.cc @@ -5,26 +5,15 @@ #include "ui/views/drag_utils.h" #include "ui/gfx/canvas.h" -#include "ui/gfx/size.h" - -#if defined(USE_AURA) -#include "ui/aura/client/drag_drop_client.h" -#include "ui/aura/root_window.h" -#include "ui/aura/window.h" #include "ui/gfx/display.h" #include "ui/gfx/screen.h" +#include "ui/gfx/size.h" #include "ui/views/widget/widget.h" -#elif defined(OS_WIN) -#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" -#else -#error -#endif + +namespace { float GetDeviceScaleForNativeView(views::Widget* widget) { float device_scale = 1.0f; -#if defined(USE_AURA) // The following code should work on other platforms as well. But we do not // yet care about device scale factor on other platforms. So to keep drag and // drop behavior on other platforms un-touched, we wrap this in the #if guard. @@ -34,34 +23,12 @@ float GetDeviceScaleForNativeView(views::Widget* widget) { GetDisplayNearestWindow(view); device_scale = display.device_scale_factor(); } -#endif return device_scale; } -namespace views { +} // namespace -void RunShellDrag(gfx::NativeView view, - const ui::OSExchangeData& data, - const gfx::Point& location, - int operation, - ui::DragDropTypes::DragEventSource source) { -#if defined(USE_AURA) - gfx::Point root_location(location); - aura::Window* root_window = view->GetRootWindow(); - aura::Window::ConvertPointToTarget(view, root_window, &root_location); - if (aura::client::GetDragDropClient(root_window)) { - aura::client::GetDragDropClient(root_window)->StartDragAndDrop( - data, root_window, view, root_location, operation, source); - } -#elif defined(OS_WIN) - scoped_refptr<ui::DragSourceWin> drag_source(new ui::DragSourceWin); - DWORD effects; - DoDragDrop(ui::OSExchangeDataProviderWin::GetIDataObject(data), - drag_source, - ui::DragDropTypes::DragOperationToDropEffect(operation), - &effects); -#endif -} +namespace views { gfx::Canvas* GetCanvasForDragImage(views::Widget* widget, const gfx::Size& canvas_size) { diff --git a/chromium/ui/views/drag_utils_aura.cc b/chromium/ui/views/drag_utils_aura.cc new file mode 100644 index 00000000000..cb64199a2a6 --- /dev/null +++ b/chromium/ui/views/drag_utils_aura.cc @@ -0,0 +1,27 @@ +// Copyright 2014 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/drag_utils.h" + +#include "ui/aura/window.h" +#include "ui/aura/window_event_dispatcher.h" +#include "ui/wm/public/drag_drop_client.h" + +namespace views { + +void RunShellDrag(gfx::NativeView view, + const ui::OSExchangeData& data, + const gfx::Point& location, + int operation, + ui::DragDropTypes::DragEventSource source) { + gfx::Point root_location(location); + aura::Window* root_window = view->GetRootWindow(); + aura::Window::ConvertPointToTarget(view, root_window, &root_location); + if (aura::client::GetDragDropClient(root_window)) { + aura::client::GetDragDropClient(root_window)->StartDragAndDrop( + data, root_window, view, root_location, operation, source); + } +} + +} // namespace views diff --git a/chromium/ui/views/event_utils.h b/chromium/ui/views/event_utils.h deleted file mode 100644 index 095487b1525..00000000000 --- a/chromium/ui/views/event_utils.h +++ /dev/null @@ -1,31 +0,0 @@ -// 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_EVENT_UTILS_H_ -#define UI_VIEWS_EVENT_UTILS_H_ - -#include "ui/gfx/native_widget_types.h" -#include "ui/views/views_export.h" - -namespace ui { -class LocatedEvent; -} - -namespace views { - -// Reposts a located event natively. Returns false when |event| could not be -// reposted. |event| should be in screen coordinates. |window| is the target -// window that the event will be forwarded to. Only some events are supported. -VIEWS_EXPORT bool RepostLocatedEvent(gfx::NativeWindow window, - const ui::LocatedEvent& event); - -#if defined(OS_WIN) && defined(USE_AURA) -// Reposts a located event to the HWND passed in. -VIEWS_EXPORT bool RepostLocatedEventWin(HWND window, - const ui::LocatedEvent& event); -#endif - -} // namespace views - -#endif // UI_VIEWS_EVENT_UTILS_H_ diff --git a/chromium/ui/views/event_utils_aura.cc b/chromium/ui/views/event_utils_aura.cc deleted file mode 100644 index e3d07d9f310..00000000000 --- a/chromium/ui/views/event_utils_aura.cc +++ /dev/null @@ -1,60 +0,0 @@ -// 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/event_utils.h" - -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "ui/aura/client/screen_position_client.h" -#include "ui/aura/root_window.h" -#include "ui/events/event.h" -#include "ui/gfx/point.h" -#include "ui/views/views_delegate.h" - -using aura::client::ScreenPositionClient; - -namespace views { - -bool RepostLocatedEvent(gfx::NativeWindow window, - const ui::LocatedEvent& event) { - if (!window) - return false; - -#if defined(OS_WIN) - if (ViewsDelegate::views_delegate && - !ViewsDelegate::views_delegate->IsWindowInMetro(window)) { - return RepostLocatedEventWin( - window->GetDispatcher()->host()->GetAcceleratedWidget(), event); - } -#endif - aura::Window* root_window = window->GetRootWindow(); - - gfx::Point root_loc(event.location()); - ScreenPositionClient* spc = - aura::client::GetScreenPositionClient(root_window); - if (!spc) - return false; - - spc->ConvertPointFromScreen(root_window, &root_loc); - - scoped_ptr<ui::LocatedEvent> relocated; - if (event.IsMouseEvent()) { - const ui::MouseEvent& orig = static_cast<const ui::MouseEvent&>(event); - relocated.reset(new ui::MouseEvent(orig)); - } else if (event.IsGestureEvent()) { - // TODO(rbyers): Gesture event repost is tricky to get right - // crbug.com/170987. - return false; - } else { - NOTREACHED(); - return false; - } - relocated->set_location(root_loc); - relocated->set_root_location(root_loc); - - root_window->GetDispatcher()->RepostEvent(*relocated); - return true; -} - -} // namespace views diff --git a/chromium/ui/views/event_utils_win.cc b/chromium/ui/views/event_utils_win.cc deleted file mode 100644 index 3e908889dfa..00000000000 --- a/chromium/ui/views/event_utils_win.cc +++ /dev/null @@ -1,64 +0,0 @@ -// 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/event_utils.h" - -#include <windowsx.h> - -#include "base/logging.h" -#include "ui/events/event.h" -#include "ui/events/event_constants.h" -#include "ui/gfx/point.h" - -namespace views { - -#if defined(USE_AURA) -bool RepostLocatedEventWin(HWND window, - const ui::LocatedEvent& event) { -#else -bool RepostLocatedEvent(gfx::NativeWindow window - const ui::LocatedEvent& event) { -#endif - if (!window) - return false; - - // Determine whether the click was in the client area or not. - // NOTE: WM_NCHITTEST coordinates are relative to the screen. - const gfx::Point screen_loc = event.location(); - LRESULT nc_hit_result = SendMessage(window, WM_NCHITTEST, 0, - MAKELPARAM(screen_loc.x(), - screen_loc.y())); - const bool in_client_area = nc_hit_result == HTCLIENT; - - // TODO(sky): this isn't right. The event to generate should correspond with - // the event we just got. MouseEvent only tells us what is down, which may - // differ. Need to add ability to get changed button from MouseEvent. - int event_type; - int flags = event.flags(); - if (flags & ui::EF_LEFT_MOUSE_BUTTON) { - event_type = in_client_area ? WM_LBUTTONDOWN : WM_NCLBUTTONDOWN; - } else if (flags & ui::EF_MIDDLE_MOUSE_BUTTON) { - event_type = in_client_area ? WM_MBUTTONDOWN : WM_NCMBUTTONDOWN; - } else if (flags & ui::EF_RIGHT_MOUSE_BUTTON) { - event_type = in_client_area ? WM_RBUTTONDOWN : WM_NCRBUTTONDOWN; - } else { - NOTREACHED(); - return false; - } - - int window_x = screen_loc.x(); - int window_y = screen_loc.y(); - if (in_client_area) { - POINT pt = {window_x, window_y}; - ScreenToClient(window, &pt); - window_x = pt.x; - window_y = pt.y; - } - - WPARAM target = in_client_area ? event.native_event().wParam : nc_hit_result; - PostMessage(window, event_type, target, MAKELPARAM(window_x, window_y)); - return true; -} - -} // namespace views diff --git a/chromium/ui/views/examples/DEPS b/chromium/ui/views/examples/DEPS index 1108a456a1a..06e440bbb91 100644 --- a/chromium/ui/views/examples/DEPS +++ b/chromium/ui/views/examples/DEPS @@ -2,4 +2,6 @@ include_rules = [ "+content/public", "+content/shell", "+sandbox", + "+ui/gl/gl_surface.h", # To initialize GL bindings. + "+ui/views_content_client", ] diff --git a/chromium/ui/views/examples/bubble_example.cc b/chromium/ui/views/examples/bubble_example.cc index 6869b4634a1..49a60e59d9e 100644 --- a/chromium/ui/views/examples/bubble_example.cc +++ b/chromium/ui/views/examples/bubble_example.cc @@ -11,6 +11,8 @@ #include "ui/views/layout/box_layout.h" #include "ui/views/widget/widget.h" +using base::ASCIIToUTF16; + namespace views { namespace examples { @@ -26,7 +28,7 @@ BubbleBorder::Arrow arrows[] = { BubbleBorder::BOTTOM_LEFT, BubbleBorder::LEFT_BOTTOM, BubbleBorder::LEFT_CENTER, BubbleBorder::LEFT_TOP }; -string16 GetArrowName(BubbleBorder::Arrow arrow) { +base::string16 GetArrowName(BubbleBorder::Arrow arrow) { switch (arrow) { case BubbleBorder::TOP_LEFT: return ASCIIToUTF16("TOP_LEFT"); case BubbleBorder::TOP_RIGHT: return ASCIIToUTF16("TOP_RIGHT"); @@ -81,8 +83,6 @@ void BubbleExample::CreateExampleView(View* container) { container->AddChildView(align_to_edge_); persistent_ = new LabelButton(this, ASCIIToUTF16("Persistent")); container->AddChildView(persistent_); - fade_in_ = new LabelButton(this, ASCIIToUTF16("Fade In")); - container->AddChildView(fade_in_); } void BubbleExample::ButtonPressed(Button* sender, const ui::Event& event) { @@ -105,19 +105,14 @@ void BubbleExample::ButtonPressed(Button* sender, const ui::Event& event) { else if (sender == small_shadow_) bubble->set_shadow(BubbleBorder::SMALL_SHADOW); - if (sender == persistent_) { + if (sender == persistent_) bubble->set_close_on_deactivate(false); - bubble->set_move_with_anchor(true); - } BubbleDelegateView::CreateBubble(bubble); if (sender == align_to_edge_) bubble->SetAlignment(BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE); - if (sender == fade_in_) - bubble->StartFade(true); - else - bubble->GetWidget()->Show(); + bubble->GetWidget()->Show(); } } // namespace examples diff --git a/chromium/ui/views/examples/bubble_example.h b/chromium/ui/views/examples/bubble_example.h index fd0008e6ed9..d9ad66de6b0 100644 --- a/chromium/ui/views/examples/bubble_example.h +++ b/chromium/ui/views/examples/bubble_example.h @@ -12,15 +12,17 @@ namespace views { namespace examples { // A Bubble example. -class BubbleExample : public ExampleBase, public ButtonListener { +class VIEWS_EXAMPLES_EXPORT BubbleExample : public ExampleBase, + public ButtonListener { public: BubbleExample(); virtual ~BubbleExample(); - // Overridden from ExampleBase. + // ExampleBase: virtual void CreateExampleView(View* container) OVERRIDE; private: + // ButtonListener: virtual void ButtonPressed(Button* sender, const ui::Event& event) OVERRIDE; Button* no_shadow_; @@ -28,7 +30,6 @@ class BubbleExample : public ExampleBase, public ButtonListener { Button* small_shadow_; Button* align_to_edge_; Button* persistent_; - Button* fade_in_; DISALLOW_COPY_AND_ASSIGN(BubbleExample); }; diff --git a/chromium/ui/views/examples/button_example.cc b/chromium/ui/views/examples/button_example.cc index a791559982a..f5ea950c887 100644 --- a/chromium/ui/views/examples/button_example.cc +++ b/chromium/ui/views/examples/button_example.cc @@ -16,6 +16,8 @@ #include "ui/views/layout/box_layout.h" #include "ui/views/view.h" +using base::ASCIIToUTF16; + namespace { const char kLabelButton[] = "Label Button"; const char kTextButton[] = "Text Button"; @@ -73,8 +75,6 @@ void ButtonExample::CreateExampleView(View* container) { rb.GetImageNamed(IDR_CLOSE_H).ToImageSkia()); image_button_->SetImage(ImageButton::STATE_PRESSED, rb.GetImageNamed(IDR_CLOSE_P).ToImageSkia()); - image_button_->SetOverlayImage(rb.GetImageNamed( - IDR_MENU_CHECK).ToImageSkia()); container->AddChildView(image_button_); } @@ -124,10 +124,13 @@ void ButtonExample::TextButtonPressed(const ui::Event& event) { text_button_->text().length() < 50 ? kLongText : kTextButton)); } else { use_native_theme_border_ = !use_native_theme_border_; - if (use_native_theme_border_) - text_button_->set_border(new TextButtonNativeThemeBorder(text_button_)); - else - text_button_->set_border(new TextButtonDefaultBorder()); + if (use_native_theme_border_) { + text_button_->SetBorder(scoped_ptr<views::Border>( + new TextButtonNativeThemeBorder(text_button_))); + } else { + text_button_->SetBorder( + scoped_ptr<views::Border>(new TextButtonDefaultBorder())); + } } } else if (event.IsAltDown()) { text_button_->SetIsDefault(!text_button_->is_default()); @@ -161,7 +164,7 @@ void ButtonExample::LabelButtonPressed(const ui::Event& event) { } } else if (event.IsShiftDown()) { if (event.IsAltDown()) { - label_button_->SetFocusable(!label_button_->focusable()); + label_button_->SetFocusable(!label_button_->IsFocusable()); } else { label_button_->SetStyle(static_cast<Button::ButtonStyle>( (label_button_->style() + 1) % Button::STYLE_COUNT)); diff --git a/chromium/ui/views/examples/button_example.h b/chromium/ui/views/examples/button_example.h index 6f23a0be1c1..d195faa6820 100644 --- a/chromium/ui/views/examples/button_example.h +++ b/chromium/ui/views/examples/button_example.h @@ -5,8 +5,7 @@ #ifndef UI_VIEWS_EXAMPLES_BUTTON_EXAMPLE_H_ #define UI_VIEWS_EXAMPLES_BUTTON_EXAMPLE_H_ -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "base/macros.h" #include "ui/views/controls/button/text_button.h" #include "ui/views/examples/example_base.h" @@ -18,19 +17,20 @@ class LabelButton; namespace examples { // ButtonExample simply counts the number of clicks. -class ButtonExample : public ExampleBase, public ButtonListener { +class VIEWS_EXAMPLES_EXPORT ButtonExample : public ExampleBase, + public ButtonListener { public: ButtonExample(); virtual ~ButtonExample(); - // Overridden from ExampleBase: + // ExampleBase: virtual void CreateExampleView(View* container) OVERRIDE; private: void TextButtonPressed(const ui::Event& event); void LabelButtonPressed(const ui::Event& event); - // Overridden from ButtonListener: + // ButtonListener: virtual void ButtonPressed(Button* sender, const ui::Event& event) OVERRIDE; // Example buttons. diff --git a/chromium/ui/views/examples/checkbox_example.cc b/chromium/ui/views/examples/checkbox_example.cc index cfe930fd0ca..b25dd8e8d86 100644 --- a/chromium/ui/views/examples/checkbox_example.cc +++ b/chromium/ui/views/examples/checkbox_example.cc @@ -20,7 +20,7 @@ CheckboxExample::~CheckboxExample() { } void CheckboxExample::CreateExampleView(View* container) { - button_ = new Checkbox(ASCIIToUTF16("Checkbox")); + button_ = new Checkbox(base::ASCIIToUTF16("Checkbox")); button_->set_listener(this); container->SetLayoutManager(new FillLayout); container->AddChildView(button_); diff --git a/chromium/ui/views/examples/checkbox_example.h b/chromium/ui/views/examples/checkbox_example.h index 1b0e52f5a3a..a5a8964513e 100644 --- a/chromium/ui/views/examples/checkbox_example.h +++ b/chromium/ui/views/examples/checkbox_example.h @@ -5,7 +5,7 @@ #ifndef UI_VIEWS_EXAMPLES_CHECKBOX_EXAMPLE_H_ #define UI_VIEWS_EXAMPLES_CHECKBOX_EXAMPLE_H_ -#include "base/basictypes.h" +#include "base/macros.h" #include "ui/views/controls/button/button.h" #include "ui/views/examples/example_base.h" @@ -15,7 +15,8 @@ class Checkbox; namespace examples { // CheckboxExample exercises a Checkbox control. -class CheckboxExample : public ExampleBase, public ButtonListener { +class VIEWS_EXAMPLES_EXPORT CheckboxExample : public ExampleBase, + public ButtonListener { public: CheckboxExample(); virtual ~CheckboxExample(); diff --git a/chromium/ui/views/examples/combobox_example.cc b/chromium/ui/views/examples/combobox_example.cc index 4c2a1b8cc26..ed07379b605 100644 --- a/chromium/ui/views/examples/combobox_example.cc +++ b/chromium/ui/views/examples/combobox_example.cc @@ -22,8 +22,8 @@ int ComboboxModelExample::GetItemCount() const { return 10; } -string16 ComboboxModelExample::GetItemAt(int index) { - return UTF8ToUTF16(base::StringPrintf("Item %d", index)); +base::string16 ComboboxModelExample::GetItemAt(int index) { + return base::UTF8ToUTF16(base::StringPrintf("Item %d", index)); } ComboboxExample::ComboboxExample() : ExampleBase("Combo Box"), combobox_(NULL) { @@ -44,9 +44,9 @@ void ComboboxExample::CreateExampleView(View* container) { container->AddChildView(combobox_); } -void ComboboxExample::OnSelectedIndexChanged(Combobox* combobox) { +void ComboboxExample::OnPerformAction(Combobox* combobox) { DCHECK_EQ(combobox_, combobox); - PrintStatus("Selected: %s", UTF16ToUTF8(combobox_model_.GetItemAt( + PrintStatus("Selected: %s", base::UTF16ToUTF8(combobox_model_.GetItemAt( combobox->selected_index())).c_str()); } diff --git a/chromium/ui/views/examples/combobox_example.h b/chromium/ui/views/examples/combobox_example.h index 263782d54e3..89ccff2e986 100644 --- a/chromium/ui/views/examples/combobox_example.h +++ b/chromium/ui/views/examples/combobox_example.h @@ -5,8 +5,7 @@ #ifndef UI_VIEWS_EXAMPLES_COMBOBOX_EXAMPLE_H_ #define UI_VIEWS_EXAMPLES_COMBOBOX_EXAMPLE_H_ -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "base/macros.h" #include "ui/base/models/combobox_model.h" #include "ui/views/controls/combobox/combobox_listener.h" #include "ui/views/examples/example_base.h" @@ -15,30 +14,31 @@ namespace views { namespace examples { // A combobox model implementation that generates a list of "Item <index>". -class ComboboxModelExample : public ui::ComboboxModel { +class VIEWS_EXAMPLES_EXPORT ComboboxModelExample : public ui::ComboboxModel { public: ComboboxModelExample(); virtual ~ComboboxModelExample(); - // Overridden from ui::ComboboxModel: + // ui::ComboboxModel: virtual int GetItemCount() const OVERRIDE; - virtual string16 GetItemAt(int index) OVERRIDE; + virtual base::string16 GetItemAt(int index) OVERRIDE; private: DISALLOW_COPY_AND_ASSIGN(ComboboxModelExample); }; -class ComboboxExample : public ExampleBase, public ComboboxListener { +class VIEWS_EXAMPLES_EXPORT ComboboxExample : public ExampleBase, + public ComboboxListener { public: ComboboxExample(); virtual ~ComboboxExample(); - // Overridden from ExampleBase: + // ExampleBase: virtual void CreateExampleView(View* container) OVERRIDE; private: - // Overridden from ComboboxListener: - virtual void OnSelectedIndexChanged(Combobox* combobox) OVERRIDE; + // ComboboxListener: + virtual void OnPerformAction(Combobox* combobox) OVERRIDE; ComboboxModelExample combobox_model_; Combobox* combobox_; diff --git a/chromium/ui/views/examples/content_client/DEPS b/chromium/ui/views/examples/content_client/DEPS deleted file mode 100644 index f71262e800a..00000000000 --- a/chromium/ui/views/examples/content_client/DEPS +++ /dev/null @@ -1,4 +0,0 @@ -include_rules = [ - "+content/public", - "+content/shell", -] diff --git a/chromium/ui/views/examples/content_client/examples_browser_main_parts.cc b/chromium/ui/views/examples/content_client/examples_browser_main_parts.cc deleted file mode 100644 index e270d6fef12..00000000000 --- a/chromium/ui/views/examples/content_client/examples_browser_main_parts.cc +++ /dev/null @@ -1,95 +0,0 @@ -// 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/examples/content_client/examples_browser_main_parts.h" - -#include "base/bind.h" -#include "base/command_line.h" -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "base/strings/string_number_conversions.h" -#include "base/threading/thread.h" -#include "base/threading/thread_restrictions.h" -#include "content/public/common/content_switches.h" -#include "content/shell/browser/shell_browser_context.h" -#include "ui/base/ime/input_method_initializer.h" -#include "ui/views/examples/examples_window_with_content.h" -#include "ui/views/focus/accelerator_handler.h" -#include "ui/views/test/desktop_test_views_delegate.h" -#include "url/gurl.h" - -#if defined(USE_AURA) -#include "ui/aura/env.h" -#include "ui/gfx/screen.h" -#include "ui/views/widget/desktop_aura/desktop_screen.h" -#include "ui/views/widget/native_widget_aura.h" -#endif - -#if defined(OS_CHROMEOS) -#include "ui/aura/root_window.h" -#include "ui/aura/test/test_screen.h" -#include "ui/aura/window.h" -#include "ui/wm/test/wm_test_helper.h" -#endif - -namespace views { -namespace examples { - -ExamplesBrowserMainParts::ExamplesBrowserMainParts( - const content::MainFunctionParams& parameters) { -} - -ExamplesBrowserMainParts::~ExamplesBrowserMainParts() { -} - -void ExamplesBrowserMainParts::PreMainMessageLoopRun() { - ui::InitializeInputMethodForTesting(); - browser_context_.reset(new content::ShellBrowserContext(false, NULL)); - - gfx::NativeView window_context = NULL; -#if defined(OS_CHROMEOS) - gfx::Screen::SetScreenInstance( - gfx::SCREEN_TYPE_NATIVE, aura::TestScreen::Create()); - // Set up basic pieces of views::corewm. - wm_test_helper_.reset(new wm::WMTestHelper(gfx::Size(800, 600))); - // Ensure the X window gets mapped. - wm_test_helper_->root_window()->host()->Show(); - // Ensure Aura knows where to open new windows. - window_context = wm_test_helper_->root_window()->window(); -#elif defined(USE_AURA) - aura::Env::CreateInstance(); - gfx::Screen::SetScreenInstance( - gfx::SCREEN_TYPE_NATIVE, CreateDesktopScreen()); -#endif - views_delegate_.reset(new DesktopTestViewsDelegate); - - ShowExamplesWindowWithContent( - QUIT_ON_CLOSE, browser_context_.get(), window_context); -} - -void ExamplesBrowserMainParts::PostMainMessageLoopRun() { - browser_context_.reset(); -#if defined(OS_CHROMEOS) - wm_test_helper_.reset(); -#endif - views_delegate_.reset(); -#if defined(USE_AURA) - aura::Env::DeleteInstance(); -#endif -} - -bool ExamplesBrowserMainParts::MainMessageLoopRun(int* result_code) { - // xxx: Hax here because this kills event handling. -#if !defined(USE_AURA) - AcceleratorHandler accelerator_handler; - base::RunLoop run_loop(&accelerator_handler); -#else - base::RunLoop run_loop; -#endif - run_loop.Run(); - return true; -} - -} // namespace examples -} // namespace views diff --git a/chromium/ui/views/examples/content_client/examples_browser_main_parts.h b/chromium/ui/views/examples/content_client/examples_browser_main_parts.h deleted file mode 100644 index c3e3172c6d5..00000000000 --- a/chromium/ui/views/examples/content_client/examples_browser_main_parts.h +++ /dev/null @@ -1,57 +0,0 @@ -// 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_EXAMPLES_CONTENT_CLIENT_EXAMPLES_BROWSER_MAIN_PARTS_H_ -#define UI_VIEWS_EXAMPLES_CONTENT_CLIENT_EXAMPLES_BROWSER_MAIN_PARTS_H_ - -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "content/public/browser/browser_main_parts.h" - -namespace content { -class ShellBrowserContext; -struct MainFunctionParams; -} - -namespace wm { -class WMTestHelper; -} - -namespace views { -class ViewsDelegate; - -namespace examples { - -class ExamplesBrowserMainParts : public content::BrowserMainParts { - public: - explicit ExamplesBrowserMainParts( - const content::MainFunctionParams& parameters); - virtual ~ExamplesBrowserMainParts(); - - // Overridden from content::BrowserMainParts: - virtual void PreMainMessageLoopRun() OVERRIDE; - virtual bool MainMessageLoopRun(int* result_code) OVERRIDE; - virtual void PostMainMessageLoopRun() OVERRIDE; - - content::ShellBrowserContext* browser_context() { - return browser_context_.get(); - } - - private: - scoped_ptr<content::ShellBrowserContext> browser_context_; - - scoped_ptr<ViewsDelegate> views_delegate_; - -#if defined(OS_CHROMEOS) - // Enable a minimal set of views::corewm to be initialized. - scoped_ptr<wm::WMTestHelper> wm_test_helper_; -#endif - - DISALLOW_COPY_AND_ASSIGN(ExamplesBrowserMainParts); -}; - -} // namespace examples -} // namespace views - -#endif // UI_VIEWS_EXAMPLES_CONTENT_CLIENT_EXAMPLES_BROWSER_MAIN_PARTS_H_ diff --git a/chromium/ui/views/examples/content_client/examples_content_browser_client.cc b/chromium/ui/views/examples/content_client/examples_content_browser_client.cc deleted file mode 100644 index 8a4ebc862e9..00000000000 --- a/chromium/ui/views/examples/content_client/examples_content_browser_client.cc +++ /dev/null @@ -1,36 +0,0 @@ -// 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/examples/content_client/examples_content_browser_client.h" - -#include "content/shell/browser/shell_browser_context.h" -#include "ui/views/examples/content_client/examples_browser_main_parts.h" - -namespace views { -namespace examples { - -ExamplesContentBrowserClient::ExamplesContentBrowserClient() - : examples_browser_main_parts_(NULL) { -} - -ExamplesContentBrowserClient::~ExamplesContentBrowserClient() { -} - -content::BrowserMainParts* ExamplesContentBrowserClient::CreateBrowserMainParts( - const content::MainFunctionParams& parameters) { - examples_browser_main_parts_ = new ExamplesBrowserMainParts(parameters); - return examples_browser_main_parts_; -} - -net::URLRequestContextGetter* -ExamplesContentBrowserClient::CreateRequestContext( - content::BrowserContext* content_browser_context, - content::ProtocolHandlerMap* protocol_handlers) { - content::ShellBrowserContext* shell_context = - examples_browser_main_parts_->browser_context(); - return shell_context->CreateRequestContext(protocol_handlers); -} - -} // namespace examples -} // namespace views diff --git a/chromium/ui/views/examples/content_client/examples_content_browser_client.h b/chromium/ui/views/examples/content_client/examples_content_browser_client.h deleted file mode 100644 index b49d2c0528e..00000000000 --- a/chromium/ui/views/examples/content_client/examples_content_browser_client.h +++ /dev/null @@ -1,44 +0,0 @@ -// 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_EXAMPLES_CONTENT_CLIENT_EXAMPLES_CONTENT_BROWSER_CLIENT_H_ -#define UI_VIEWS_EXAMPLES_CONTENT_CLIENT_EXAMPLES_CONTENT_BROWSER_CLIENT_H_ - -#include <string> - -#include "base/compiler_specific.h" -#include "base/memory/scoped_ptr.h" -#include "content/public/browser/content_browser_client.h" - -namespace content { -class ShellBrowserContext; -} - -namespace views { -namespace examples { - -class ExamplesBrowserMainParts; - -class ExamplesContentBrowserClient : public content::ContentBrowserClient { - public: - ExamplesContentBrowserClient(); - virtual ~ExamplesContentBrowserClient(); - - // Overridden from content::ContentBrowserClient: - virtual content::BrowserMainParts* CreateBrowserMainParts( - const content::MainFunctionParams& parameters) OVERRIDE; - virtual net::URLRequestContextGetter* CreateRequestContext( - content::BrowserContext* browser_context, - content::ProtocolHandlerMap* protocol_handlers) OVERRIDE; - - private: - ExamplesBrowserMainParts* examples_browser_main_parts_; - - DISALLOW_COPY_AND_ASSIGN(ExamplesContentBrowserClient); -}; - -} // namespace examples -} // namespace views - -#endif // UI_VIEWS_EXAMPLES_CONTENT_CLIENT_EXAMPLES_CONTENT_BROWSER_CLIENT_H_ diff --git a/chromium/ui/views/examples/content_client/examples_main.cc b/chromium/ui/views/examples/content_client/examples_main.cc deleted file mode 100644 index bb2b059b71e..00000000000 --- a/chromium/ui/views/examples/content_client/examples_main.cc +++ /dev/null @@ -1,25 +0,0 @@ -// 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 "content/public/app/content_main.h" -#include "sandbox/win/src/sandbox_types.h" -#include "ui/views/examples/content_client/examples_main_delegate.h" - -#if defined(OS_WIN) -#include "content/public/app/startup_helper_win.h" -#endif - -#if defined(OS_WIN) -int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t*, int) { - sandbox::SandboxInterfaceInfo sandbox_info = {0}; - content::InitializeSandboxInfo(&sandbox_info); - views::examples::ExamplesMainDelegate delegate; - return content::ContentMain(instance, &sandbox_info, &delegate); -} -#else -int main(int argc, const char** argv) { - views::examples::ExamplesMainDelegate delegate; - return content::ContentMain(argc, argv, &delegate); -} -#endif diff --git a/chromium/ui/views/examples/content_client/examples_main_delegate.cc b/chromium/ui/views/examples/content_client/examples_main_delegate.cc deleted file mode 100644 index aabd289d868..00000000000 --- a/chromium/ui/views/examples/content_client/examples_main_delegate.cc +++ /dev/null @@ -1,80 +0,0 @@ -// 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/examples/content_client/examples_main_delegate.h" - -#include <string> - -#include "base/command_line.h" -#include "base/files/file_path.h" -#include "base/logging.h" -#include "base/path_service.h" -#include "content/public/common/content_switches.h" -#include "ui/base/resource/resource_bundle.h" -#include "ui/base/ui_base_paths.h" -#include "ui/views/examples/content_client/examples_content_browser_client.h" - -#if defined(OS_WIN) -#include "base/logging_win.h" -#endif - -namespace views { -namespace examples { -namespace { - -#if defined(OS_WIN) -// {83FAC8EE-7A0E-4dbb-A3F6-6F500D7CAB1A} -const GUID kViewsExamplesProviderName = - { 0x83fac8ee, 0x7a0e, 0x4dbb, - { 0xa3, 0xf6, 0x6f, 0x50, 0xd, 0x7c, 0xab, 0x1a } }; -#endif - -} // namespace - -ExamplesMainDelegate::ExamplesMainDelegate() { -} - -ExamplesMainDelegate::~ExamplesMainDelegate() { -} - -bool ExamplesMainDelegate::BasicStartupComplete(int* exit_code) { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); - std::string process_type = - command_line.GetSwitchValueASCII(switches::kProcessType); - - content::SetContentClient(&content_client_); - - logging::LoggingSettings settings; - settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; - bool success = logging::InitLogging(settings); - CHECK(success); -#if defined(OS_WIN) - logging::LogEventProvider::Initialize(kViewsExamplesProviderName); -#endif - - return false; -} - -void ExamplesMainDelegate::PreSandboxStartup() { - InitializeResourceBundle(); -} - -content::ContentBrowserClient* - ExamplesMainDelegate::CreateContentBrowserClient() { - browser_client_.reset(new ExamplesContentBrowserClient); - return browser_client_.get(); -} - -void ExamplesMainDelegate::InitializeResourceBundle() { - base::FilePath pak_dir; - PathService::Get(base::DIR_MODULE, &pak_dir); - - base::FilePath pak_file; - pak_file = pak_dir.Append(FILE_PATH_LITERAL("ui_test.pak")); - - ui::ResourceBundle::InitSharedInstanceWithPakPath(pak_file); -} - -} // namespace examples -} // namespace views diff --git a/chromium/ui/views/examples/content_client/examples_main_delegate.h b/chromium/ui/views/examples/content_client/examples_main_delegate.h deleted file mode 100644 index e8cb493f613..00000000000 --- a/chromium/ui/views/examples/content_client/examples_main_delegate.h +++ /dev/null @@ -1,43 +0,0 @@ -// 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_EXAMPLES_CONTENT_CLIENT_EXAMPLES_MAIN_DELEGATE_H_ -#define UI_VIEWS_EXAMPLES_CONTENT_CLIENT_EXAMPLES_MAIN_DELEGATE_H_ - -#include <string> - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/memory/scoped_ptr.h" -#include "content/public/app/content_main_delegate.h" -#include "content/shell/common/shell_content_client.h" - -namespace views { -namespace examples { - -class ExamplesContentBrowserClient; - -class ExamplesMainDelegate : public content::ContentMainDelegate { - public: - ExamplesMainDelegate(); - virtual ~ExamplesMainDelegate(); - - // content::ContentMainDelegate implementation - virtual bool BasicStartupComplete(int* exit_code) OVERRIDE; - virtual void PreSandboxStartup() OVERRIDE; - virtual content::ContentBrowserClient* CreateContentBrowserClient() OVERRIDE; - - private: - void InitializeResourceBundle(); - - scoped_ptr<ExamplesContentBrowserClient> browser_client_; - content::ShellContentClient content_client_; - - DISALLOW_COPY_AND_ASSIGN(ExamplesMainDelegate); -}; - -} // namespace examples -} // namespace views - -#endif // UI_VIEWS_EXAMPLES_CONTENT_CLIENT_EXAMPLES_MAIN_DELEGATE_H_ diff --git a/chromium/ui/views/examples/double_split_view_example.cc b/chromium/ui/views/examples/double_split_view_example.cc index fb1f099a1f9..8ff40df66a8 100644 --- a/chromium/ui/views/examples/double_split_view_example.cc +++ b/chromium/ui/views/examples/double_split_view_example.cc @@ -21,8 +21,8 @@ class SplittedView : public View { void SetColor(SkColor from, SkColor to); - // Overridden from View. - virtual gfx::Size GetMinimumSize() OVERRIDE; + // View: + virtual gfx::Size GetMinimumSize() const OVERRIDE; private: DISALLOW_COPY_AND_ASSIGN(SplittedView); @@ -39,7 +39,7 @@ void SplittedView::SetColor(SkColor from, SkColor to) { set_background(Background::CreateVerticalGradientBackground(from, to)); } -gfx::Size SplittedView::GetMinimumSize() { +gfx::Size SplittedView::GetMinimumSize() const { return gfx::Size(10, 10); } diff --git a/chromium/ui/views/examples/double_split_view_example.h b/chromium/ui/views/examples/double_split_view_example.h index 7fbae46b212..fe37c792657 100644 --- a/chromium/ui/views/examples/double_split_view_example.h +++ b/chromium/ui/views/examples/double_split_view_example.h @@ -5,8 +5,7 @@ #ifndef UI_VIEWS_EXAMPLES_DOUBLE_SPLIT_VIEW_EXAMPLE_H_ #define UI_VIEWS_EXAMPLES_DOUBLE_SPLIT_VIEW_EXAMPLE_H_ -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "base/macros.h" #include "ui/views/examples/example_base.h" namespace views { @@ -14,12 +13,12 @@ class SingleSplitView; namespace examples { -class DoubleSplitViewExample : public ExampleBase { +class VIEWS_EXAMPLES_EXPORT DoubleSplitViewExample : public ExampleBase { public: DoubleSplitViewExample(); virtual ~DoubleSplitViewExample(); - // Overridden from ExampleBase: + // ExampleBase: virtual void CreateExampleView(View* container) OVERRIDE; private: diff --git a/chromium/ui/views/examples/example_base.cc b/chromium/ui/views/examples/example_base.cc index 66a6c854c4a..a06fc1dcfea 100644 --- a/chromium/ui/views/examples/example_base.cc +++ b/chromium/ui/views/examples/example_base.cc @@ -6,7 +6,6 @@ #include <stdarg.h> -#include "base/compiler_specific.h" #include "base/strings/stringprintf.h" #include "ui/views/view.h" @@ -28,7 +27,7 @@ class ContainerView : public View { } private: - // Overridden from View: + // View: virtual void ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) OVERRIDE { View::ViewHierarchyChanged(details); diff --git a/chromium/ui/views/examples/example_base.h b/chromium/ui/views/examples/example_base.h index a17cc0d0f91..f841eb920b1 100644 --- a/chromium/ui/views/examples/example_base.h +++ b/chromium/ui/views/examples/example_base.h @@ -7,14 +7,15 @@ #include <string> -#include "base/basictypes.h" +#include "base/macros.h" +#include "ui/views/examples/views_examples_export.h" namespace views { class View; namespace examples { -class ExampleBase { +class VIEWS_EXAMPLES_EXPORT ExampleBase { public: virtual ~ExampleBase(); diff --git a/chromium/ui/views/examples/example_combobox_model.cc b/chromium/ui/views/examples/example_combobox_model.cc index 255ef15af96..a1160e7bb0e 100644 --- a/chromium/ui/views/examples/example_combobox_model.cc +++ b/chromium/ui/views/examples/example_combobox_model.cc @@ -20,8 +20,8 @@ int ExampleComboboxModel::GetItemCount() const { return count_; } -string16 ExampleComboboxModel::GetItemAt(int index) { - return ASCIIToUTF16(strings_[index]); +base::string16 ExampleComboboxModel::GetItemAt(int index) { + return base::ASCIIToUTF16(strings_[index]); } } // namespace examples diff --git a/chromium/ui/views/examples/example_combobox_model.h b/chromium/ui/views/examples/example_combobox_model.h index 74262d8b90f..3b233323ef5 100644 --- a/chromium/ui/views/examples/example_combobox_model.h +++ b/chromium/ui/views/examples/example_combobox_model.h @@ -5,8 +5,7 @@ #ifndef UI_VIEWS_EXAMPLES_EXAMPLE_COMBOBOX_MODEL_H_ #define UI_VIEWS_EXAMPLES_EXAMPLE_COMBOBOX_MODEL_H_ -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "base/macros.h" #include "ui/base/models/combobox_model.h" namespace views { @@ -17,9 +16,9 @@ class ExampleComboboxModel : public ui::ComboboxModel { ExampleComboboxModel(const char** strings, int count); virtual ~ExampleComboboxModel(); - // Overridden from ui::ComboboxModel: + // ui::ComboboxModel: virtual int GetItemCount() const OVERRIDE; - virtual string16 GetItemAt(int index) OVERRIDE; + virtual base::string16 GetItemAt(int index) OVERRIDE; private: const char** strings_; diff --git a/chromium/ui/views/examples/examples.gyp b/chromium/ui/views/examples/examples.gyp new file mode 100644 index 00000000000..4cf3a1bcf6b --- /dev/null +++ b/chromium/ui/views/examples/examples.gyp @@ -0,0 +1,187 @@ +# Copyright 2014 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. +{ + 'variables': { + 'chromium_code': 1, + }, + 'targets': [ + { + 'target_name': 'views_examples_lib', + 'type': '<(component)', + 'dependencies': [ + '../../../base/base.gyp:base', + '../../../skia/skia.gyp:skia', + '../../../third_party/icu/icu.gyp:icui18n', + '../../../third_party/icu/icu.gyp:icuuc', + '../../base/ui_base.gyp:ui_base', + '../../events/events.gyp:events', + '../../gfx/gfx.gyp:gfx', + '../../gfx/gfx.gyp:gfx_geometry', + '../../resources/ui_resources.gyp:ui_resources', + '../../resources/ui_resources.gyp:ui_test_pak', + '../views.gyp:views', + ], + 'include_dirs': [ + '../../..', + ], + 'defines': [ + 'VIEWS_EXAMPLES_IMPLEMENTATION', + ], + 'sources': [ + 'bubble_example.cc', + 'bubble_example.h', + 'button_example.cc', + 'button_example.h', + 'checkbox_example.cc', + 'checkbox_example.h', + 'combobox_example.cc', + 'combobox_example.h', + 'double_split_view_example.cc', + 'double_split_view_example.h', + 'example_base.cc', + 'example_base.h', + 'example_combobox_model.cc', + 'example_combobox_model.h', + 'examples_window.cc', + 'examples_window.h', + 'label_example.cc', + 'label_example.h', + 'link_example.cc', + 'link_example.h', + 'message_box_example.cc', + 'message_box_example.h', + 'menu_example.cc', + 'menu_example.h', + 'multiline_example.cc', + 'multiline_example.h', + 'progress_bar_example.cc', + 'progress_bar_example.h', + 'radio_button_example.cc', + 'radio_button_example.h', + 'scroll_view_example.cc', + 'scroll_view_example.h', + 'single_split_view_example.cc', + 'single_split_view_example.h', + 'slider_example.cc', + 'slider_example.h', + 'tabbed_pane_example.cc', + 'tabbed_pane_example.h', + 'table_example.cc', + 'table_example.h', + 'text_example.cc', + 'text_example.h', + 'textfield_example.cc', + 'textfield_example.h', + 'throbber_example.cc', + 'throbber_example.h', + 'tree_view_example.cc', + 'tree_view_example.h', + 'views_examples_export.h', + 'widget_example.cc', + 'widget_example.h', + ], + 'conditions': [ + ['OS=="win"', { + 'include_dirs': [ + '../../../third_party/wtl/include', + ], + # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. + 'msvs_disabled_warnings': [ 4267, ], + }], + ['use_aura==1', { + 'dependencies': [ + '../../aura/aura.gyp:aura', + ], + }], + ], + }, # target_name: views_examples_lib + { + 'target_name': 'views_examples_exe', + 'type': 'executable', + 'dependencies': [ + '../../../base/base.gyp:base', + '../../../base/base.gyp:base_i18n', + '../../base/ui_base.gyp:ui_base', + '../../compositor/compositor.gyp:compositor', + '../../compositor/compositor.gyp:compositor_test_support', + '../../gfx/gfx.gyp:gfx', + '../../resources/ui_resources.gyp:ui_test_pak', + '../views.gyp:views', + '../views.gyp:views_test_support', + 'views_examples_lib', + ], + 'sources': [ + 'examples_main.cc', + ], + 'conditions': [ + ['use_aura==1', { + 'dependencies': [ + '../../aura/aura.gyp:aura', + ], + }], + ], + }, # target_name: views_examples_exe + { + 'target_name': 'views_examples_with_content_lib', + 'type': '<(component)', + 'dependencies': [ + '../../../base/base.gyp:base', + '../../../content/content.gyp:content', + '../../../skia/skia.gyp:skia', + '../../../url/url.gyp:url_lib', + '../../events/events.gyp:events', + '../controls/webview/webview.gyp:webview', + '../views.gyp:views', + 'views_examples_lib', + ], + 'defines': [ + 'VIEWS_EXAMPLES_WITH_CONTENT_IMPLEMENTATION', + ], + 'sources': [ + 'examples_window_with_content.cc', + 'examples_window_with_content.h', + 'views_examples_with_content_export.h', + 'webview_example.cc', + 'webview_example.h', + ], + }, # target_name: views_examples_with_content_lib + { + 'target_name': 'views_examples_with_content_exe', + 'type': 'executable', + 'dependencies': [ + '../../../base/base.gyp:base', + '../../../content/content.gyp:content', + '../../views_content_client/views_content_client.gyp:views_content_client', + 'views_examples_with_content_lib', + ], + 'sources': [ + '../../../content/app/startup_helper_win.cc', + 'examples_with_content_main_exe.cc', + ], + 'conditions': [ + ['OS=="win"', { + 'link_settings': { + 'libraries': [ + '-limm32.lib', + '-loleacc.lib', + ] + }, + 'msvs_settings': { + 'VCManifestTool': { + 'AdditionalManifestFiles': [ + 'views_examples.exe.manifest', + ], + }, + 'VCLinkerTool': { + 'SubSystem': '2', # Set /SUBSYSTEM:WINDOWS + }, + }, + 'dependencies': [ + '../../../sandbox/sandbox.gyp:sandbox', + ], + }], + ], + }, # target_name: views_examples_with_content_exe + ], +} diff --git a/chromium/ui/views/examples/examples_main.cc b/chromium/ui/views/examples/examples_main.cc index 28864c477c8..4dc9fbec153 100644 --- a/chromium/ui/views/examples/examples_main.cc +++ b/chromium/ui/views/examples/examples_main.cc @@ -4,16 +4,94 @@ #include "base/at_exit.h" #include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/i18n/icu_util.h" #include "base/message_loop/message_loop.h" +#include "base/path_service.h" #include "base/run_loop.h" +#include "ui/aura/env.h" +#include "ui/base/ime/input_method_initializer.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/base/ui_base_paths.h" +#include "ui/compositor/test/in_process_context_factory.h" +#include "ui/gfx/screen.h" +#include "ui/gl/gl_surface.h" +#include "ui/views/examples/example_base.h" +#include "ui/views/examples/examples_window.h" +#include "ui/views/test/desktop_test_views_delegate.h" +#include "ui/wm/core/wm_state.h" + +#if !defined(OS_CHROMEOS) +#include "ui/views/widget/desktop_aura/desktop_screen.h" +#endif + +#if defined(OS_WIN) +#include "ui/base/win/scoped_ole_initializer.h" +#endif + +#if defined(USE_X11) +#include "ui/gfx/x/x11_connection.h" +#endif int main(int argc, char** argv) { - base::AtExitManager at_exit; +#if defined(OS_WIN) + ui::ScopedOleInitializer ole_initializer_; +#endif + CommandLine::Init(argc, argv); - base::MessageLoop message_loop(base::MessageLoop::TYPE_UI); + base::AtExitManager at_exit; + +#if defined(USE_X11) + // This demo uses InProcessContextFactory which uses X on a separate Gpu + // thread. + gfx::InitializeThreadedX11(); +#endif + + gfx::GLSurface::InitializeOneOff(); + + // The ContextFactory must exist before any Compositors are created. + scoped_ptr<ui::InProcessContextFactory> context_factory( + new ui::InProcessContextFactory()); + + base::MessageLoopForUI message_loop; + + base::i18n::InitializeICU(); + + ui::RegisterPathProvider(); + + base::FilePath ui_test_pak_path; + DCHECK(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path)); + ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path); + + aura::Env::CreateInstance(true); + aura::Env::GetInstance()->set_context_factory(context_factory.get()); + + ui::InitializeInputMethodForTesting(); + + { + views::DesktopTestViewsDelegate views_delegate; + wm::WMState wm_state; + +#if !defined(OS_CHROMEOS) + scoped_ptr<gfx::Screen> desktop_screen(views::CreateDesktopScreen()); + gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, + desktop_screen.get()); +#endif + + views::examples::ShowExamplesWindow( + views::examples::QUIT_ON_CLOSE, + NULL, + scoped_ptr<ScopedVector<views::examples::ExampleBase> >()); + + base::RunLoop().Run(); + + ui::ResourceBundle::CleanupSharedInstance(); + } + + ui::ShutdownInputMethod(); - base::RunLoop().Run(); + aura::Env::DeleteInstance(); return 0; } diff --git a/chromium/ui/views/examples/examples_window.cc b/chromium/ui/views/examples/examples_window.cc index 041199286d1..55474440723 100644 --- a/chromium/ui/views/examples/examples_window.cc +++ b/chromium/ui/views/examples/examples_window.cc @@ -4,6 +4,7 @@ #include "ui/views/examples/examples_window.h" +#include <algorithm> #include <string> #include "base/memory/scoped_vector.h" @@ -43,26 +44,76 @@ namespace views { namespace examples { +typedef scoped_ptr<ScopedVector<ExampleBase> > ScopedExamples; + +namespace { + +// Creates the default set of examples. Caller owns the result. +ScopedExamples CreateExamples() { + ScopedExamples examples(new ScopedVector<ExampleBase>); + examples->push_back(new BubbleExample); + examples->push_back(new ButtonExample); + examples->push_back(new CheckboxExample); + examples->push_back(new ComboboxExample); + examples->push_back(new DoubleSplitViewExample); + examples->push_back(new LabelExample); + examples->push_back(new LinkExample); + examples->push_back(new MenuExample); + examples->push_back(new MessageBoxExample); + examples->push_back(new MultilineExample); + examples->push_back(new ProgressBarExample); + examples->push_back(new RadioButtonExample); + examples->push_back(new ScrollViewExample); + examples->push_back(new SingleSplitViewExample); + examples->push_back(new SliderExample); + examples->push_back(new TabbedPaneExample); + examples->push_back(new TableExample); + examples->push_back(new TextExample); + examples->push_back(new TextfieldExample); + examples->push_back(new ThrobberExample); + examples->push_back(new TreeViewExample); + examples->push_back(new WidgetExample); + return examples.Pass(); +} + +struct ExampleTitleCompare { + bool operator() (ExampleBase* a, ExampleBase* b) { + return a->example_title() < b->example_title(); + } +}; + +ScopedExamples GetExamplesToShow(ScopedExamples extra) { + ScopedExamples examples(CreateExamples()); + if (extra.get()) { + examples->insert(examples->end(), extra->begin(), extra->end()); + extra->weak_clear(); + } + std::sort(examples->begin(), examples->end(), ExampleTitleCompare()); + return examples.Pass(); +} + +} // namespace + // Model for the examples that are being added via AddExample(). class ComboboxModelExampleList : public ui::ComboboxModel { public: ComboboxModelExampleList() {} virtual ~ComboboxModelExampleList() {} - // Overridden from ui::ComboboxModel: + void SetExamples(ScopedExamples examples) { + example_list_.swap(*examples); + } + + // ui::ComboboxModel: virtual int GetItemCount() const OVERRIDE { return example_list_.size(); } - virtual string16 GetItemAt(int index) OVERRIDE { - return UTF8ToUTF16(example_list_[index]->example_title()); + virtual base::string16 GetItemAt(int index) OVERRIDE { + return base::UTF8ToUTF16(example_list_[index]->example_title()); } View* GetItemViewAt(int index) { return example_list_[index]->example_view(); } - void AddExample(ExampleBase* example) { - example_list_.push_back(example); - } - private: ScopedVector<ExampleBase> example_list_; @@ -72,14 +123,40 @@ class ComboboxModelExampleList : public ui::ComboboxModel { class ExamplesWindowContents : public WidgetDelegateView, public ComboboxListener { public: - ExamplesWindowContents(Operation operation) + ExamplesWindowContents(Operation operation, ScopedExamples examples) : combobox_(new Combobox(&combobox_model_)), example_shown_(new View), status_label_(new Label), operation_(operation) { instance_ = this; combobox_->set_listener(this); + combobox_model_.SetExamples(examples.Pass()); + combobox_->ModelChanged(); + + set_background(Background::CreateStandardPanelBackground()); + GridLayout* layout = new GridLayout(this); + SetLayoutManager(layout); + ColumnSet* column_set = layout->AddColumnSet(0); + column_set->AddPaddingColumn(0, 5); + column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, + GridLayout::USE_PREF, 0, 0); + column_set->AddPaddingColumn(0, 5); + layout->AddPaddingRow(0, 5); + layout->StartRow(0 /* no expand */, 0); + layout->AddView(combobox_); + + if (combobox_model_.GetItemCount() > 0) { + layout->StartRow(1, 0); + example_shown_->SetLayoutManager(new FillLayout()); + example_shown_->AddChildView(combobox_model_.GetItemViewAt(0)); + layout->AddView(example_shown_); + } + + layout->StartRow(0 /* no expand */, 0); + layout->AddView(status_label_); + layout->AddPaddingRow(0, 5); } + virtual ~ExamplesWindowContents() { // Delete |combobox_| first as it references |combobox_model_|. delete combobox_; @@ -88,17 +165,17 @@ class ExamplesWindowContents : public WidgetDelegateView, // Prints a message in the status area, at the bottom of the window. void SetStatus(const std::string& status) { - status_label_->SetText(UTF8ToUTF16(status)); + status_label_->SetText(base::UTF8ToUTF16(status)); } static ExamplesWindowContents* instance() { return instance_; } private: - // Overridden from WidgetDelegateView: + // WidgetDelegateView: virtual bool CanResize() const OVERRIDE { return true; } virtual bool CanMaximize() const OVERRIDE { return true; } - virtual string16 GetWindowTitle() const OVERRIDE { - return ASCIIToUTF16("Views Examples"); + virtual base::string16 GetWindowTitle() const OVERRIDE { + return base::ASCIIToUTF16("Views Examples"); } virtual View* GetContentsView() OVERRIDE { return this; } virtual void WindowClosing() OVERRIDE { @@ -107,15 +184,8 @@ class ExamplesWindowContents : public WidgetDelegateView, base::MessageLoopForUI::current()->Quit(); } - // Overridden from View: - virtual void ViewHierarchyChanged( - const ViewHierarchyChangedDetails& details) OVERRIDE { - if (details.is_add && details.child == this) - InitExamplesWindow(); - } - - // Overridden from ComboboxListener: - virtual void OnSelectedIndexChanged(Combobox* combobox) OVERRIDE { + // ComboboxListener: + virtual void OnPerformAction(Combobox* combobox) OVERRIDE { DCHECK_EQ(combobox, combobox_); DCHECK(combobox->selected_index() < combobox_model_.GetItemCount()); example_shown_->RemoveAllChildViews(false); @@ -126,61 +196,6 @@ class ExamplesWindowContents : public WidgetDelegateView, Layout(); } - // Creates the layout within the examples window. - void InitExamplesWindow() { - AddExamples(); - - set_background(Background::CreateStandardPanelBackground()); - GridLayout* layout = new GridLayout(this); - SetLayoutManager(layout); - ColumnSet* column_set = layout->AddColumnSet(0); - column_set->AddPaddingColumn(0, 5); - column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, - GridLayout::USE_PREF, 0, 0); - column_set->AddPaddingColumn(0, 5); - layout->AddPaddingRow(0, 5); - layout->StartRow(0 /* no expand */, 0); - layout->AddView(combobox_); - - if (combobox_model_.GetItemCount() > 0) { - layout->StartRow(1, 0); - example_shown_->SetLayoutManager(new FillLayout()); - example_shown_->AddChildView(combobox_model_.GetItemViewAt(0)); - layout->AddView(example_shown_); - } - - layout->StartRow(0 /* no expand */, 0); - layout->AddView(status_label_); - layout->AddPaddingRow(0, 5); - } - - // Adds all the individual examples to the combobox model. - void AddExamples() { - // Please keep this list in alphabetical order! - combobox_model_.AddExample(new BubbleExample); - combobox_model_.AddExample(new ButtonExample); - combobox_model_.AddExample(new CheckboxExample); - combobox_model_.AddExample(new ComboboxExample); - combobox_model_.AddExample(new DoubleSplitViewExample); - combobox_model_.AddExample(new LabelExample); - combobox_model_.AddExample(new LinkExample); - combobox_model_.AddExample(new MenuExample); - combobox_model_.AddExample(new MessageBoxExample); - combobox_model_.AddExample(new MultilineExample); - combobox_model_.AddExample(new ProgressBarExample); - combobox_model_.AddExample(new RadioButtonExample); - combobox_model_.AddExample(new ScrollViewExample); - combobox_model_.AddExample(new SingleSplitViewExample); - combobox_model_.AddExample(new SliderExample); - combobox_model_.AddExample(new TabbedPaneExample); - combobox_model_.AddExample(new TableExample); - combobox_model_.AddExample(new TextExample); - combobox_model_.AddExample(new TextfieldExample); - combobox_model_.AddExample(new ThrobberExample); - combobox_model_.AddExample(new TreeViewExample); - combobox_model_.AddExample(new WidgetExample); - } - static ExamplesWindowContents* instance_; ComboboxModelExampleList combobox_model_; Combobox* combobox_; @@ -194,12 +209,21 @@ class ExamplesWindowContents : public WidgetDelegateView, // static ExamplesWindowContents* ExamplesWindowContents::instance_ = NULL; -void ShowExamplesWindow(Operation operation) { +void ShowExamplesWindow(Operation operation, + gfx::NativeView window_context, + ScopedExamples extra_examples) { if (ExamplesWindowContents::instance()) { ExamplesWindowContents::instance()->GetWidget()->Activate(); } else { - Widget::CreateWindowWithBounds(new ExamplesWindowContents(operation), - gfx::Rect(0, 0, 850, 300))->Show(); + ScopedExamples examples(GetExamplesToShow(extra_examples.Pass())); + Widget* widget = new Widget; + Widget::InitParams params; + params.delegate = new ExamplesWindowContents(operation, examples.Pass()); + params.context = window_context; + params.bounds = gfx::Rect(0, 0, 850, 300); + params.remove_standard_frame = true; + widget->Init(params); + widget->Show(); } } diff --git a/chromium/ui/views/examples/examples_window.h b/chromium/ui/views/examples/examples_window.h index 94139db4d09..89222ef84a0 100644 --- a/chromium/ui/views/examples/examples_window.h +++ b/chromium/ui/views/examples/examples_window.h @@ -5,18 +5,32 @@ #ifndef UI_VIEWS_EXAMPLES_EXAMPLES_WINDOW_H_ #define UI_VIEWS_EXAMPLES_EXAMPLES_WINDOW_H_ +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "ui/gfx/native_widget_types.h" #include "ui/views/examples/views_examples_export.h" +namespace aura { +class Window; +} + namespace views { namespace examples { +class ExampleBase; + enum Operation { DO_NOTHING_ON_CLOSE = 0, QUIT_ON_CLOSE, }; -// Shows a window with the views examples in it. -VIEWS_EXAMPLES_EXPORT void ShowExamplesWindow(Operation operation); +// Shows a window with the views examples in it. |extra_examples| contains any +// additional examples to add. |window_context| is used to determine where the +// window should be created (see |Widget::InitParams::context| for details). +VIEWS_EXAMPLES_EXPORT void ShowExamplesWindow( + Operation operation, + gfx::NativeView window_context, + scoped_ptr<ScopedVector<ExampleBase> > extra_examples); } // namespace examples } // namespace views diff --git a/chromium/ui/views/examples/examples_window_with_content.cc b/chromium/ui/views/examples/examples_window_with_content.cc index 3e4e4bd357f..66342ca0e5b 100644 --- a/chromium/ui/views/examples/examples_window_with_content.cc +++ b/chromium/ui/views/examples/examples_window_with_content.cc @@ -4,221 +4,19 @@ #include "ui/views/examples/examples_window_with_content.h" -#include <string> - -#include "base/memory/scoped_vector.h" -#include "base/strings/utf_string_conversions.h" #include "content/public/browser/browser_context.h" -#include "ui/base/models/combobox_model.h" -#include "ui/base/ui_base_paths.h" -#include "ui/views/background.h" -#include "ui/views/controls/combobox/combobox.h" -#include "ui/views/controls/label.h" -#include "ui/views/examples/bubble_example.h" -#include "ui/views/examples/button_example.h" -#include "ui/views/examples/checkbox_example.h" -#include "ui/views/examples/combobox_example.h" -#include "ui/views/examples/double_split_view_example.h" -#include "ui/views/examples/label_example.h" -#include "ui/views/examples/link_example.h" -#include "ui/views/examples/menu_example.h" -#include "ui/views/examples/message_box_example.h" -#include "ui/views/examples/multiline_example.h" -#include "ui/views/examples/progress_bar_example.h" -#include "ui/views/examples/radio_button_example.h" -#include "ui/views/examples/scroll_view_example.h" -#include "ui/views/examples/single_split_view_example.h" -#include "ui/views/examples/slider_example.h" -#include "ui/views/examples/tabbed_pane_example.h" -#include "ui/views/examples/table_example.h" -#include "ui/views/examples/text_example.h" -#include "ui/views/examples/textfield_example.h" -#include "ui/views/examples/throbber_example.h" -#include "ui/views/examples/tree_view_example.h" #include "ui/views/examples/webview_example.h" -#include "ui/views/examples/widget_example.h" -#include "ui/views/layout/fill_layout.h" -#include "ui/views/layout/grid_layout.h" -#include "ui/views/widget/widget.h" -#include "ui/views/widget/widget_delegate.h" namespace views { namespace examples { -// Model for the examples that are being added via AddExample(). -class ComboboxModelExampleList : public ui::ComboboxModel { - public: - ComboboxModelExampleList() {} - virtual ~ComboboxModelExampleList() {} - - // Overridden from ui::ComboboxModel: - virtual int GetItemCount() const OVERRIDE { return example_list_.size(); } - virtual string16 GetItemAt(int index) OVERRIDE { - return UTF8ToUTF16(example_list_[index]->example_title()); - } - - View* GetItemViewAt(int index) { - return example_list_[index]->example_view(); - } - - void AddExample(ExampleBase* example) { - example_list_.push_back(example); - } - - private: - ScopedVector<ExampleBase> example_list_; - - DISALLOW_COPY_AND_ASSIGN(ComboboxModelExampleList); -}; - -class ExamplesWindowContents : public WidgetDelegateView, - public ComboboxListener { - public: - ExamplesWindowContents(Operation operation, - content::BrowserContext* browser_context) - : combobox_(new Combobox(&combobox_model_)), - example_shown_(new View), - status_label_(new Label), - operation_(operation), - browser_context_(browser_context) { - instance_ = this; - combobox_->set_listener(this); - } - virtual ~ExamplesWindowContents() { - // Delete |combobox_| first as it references |combobox_model_|. - delete combobox_; - combobox_ = NULL; - } - - // Prints a message in the status area, at the bottom of the window. - void SetStatus(const std::string& status) { - status_label_->SetText(UTF8ToUTF16(status)); - } - - static ExamplesWindowContents* instance() { return instance_; } - - private: - // Overridden from WidgetDelegateView: - virtual bool CanResize() const OVERRIDE { return true; } - virtual bool CanMaximize() const OVERRIDE { return true; } - virtual string16 GetWindowTitle() const OVERRIDE { - return ASCIIToUTF16("Views Examples"); - } - virtual View* GetContentsView() OVERRIDE { return this; } - virtual void WindowClosing() OVERRIDE { - instance_ = NULL; - if (operation_ == QUIT_ON_CLOSE) - base::MessageLoopForUI::current()->Quit(); - } - - // Overridden from View: - virtual void ViewHierarchyChanged( - const ViewHierarchyChangedDetails& details) OVERRIDE { - if (details.is_add && details.child == this) - InitExamplesWindow(); - } - - // Overridden from ComboboxListener: - virtual void OnSelectedIndexChanged(Combobox* combobox) OVERRIDE { - DCHECK_EQ(combobox, combobox_); - DCHECK(combobox->selected_index() < combobox_model_.GetItemCount()); - example_shown_->RemoveAllChildViews(false); - example_shown_->AddChildView(combobox_model_.GetItemViewAt( - combobox->selected_index())); - SetStatus(std::string()); - Layout(); - SchedulePaint(); - } - - // Creates the layout within the examples window. - void InitExamplesWindow() { - AddExamples(); - - set_background(Background::CreateStandardPanelBackground()); - GridLayout* layout = new GridLayout(this); - SetLayoutManager(layout); - ColumnSet* column_set = layout->AddColumnSet(0); - column_set->AddPaddingColumn(0, 5); - column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, - GridLayout::USE_PREF, 0, 0); - column_set->AddPaddingColumn(0, 5); - layout->AddPaddingRow(0, 5); - layout->StartRow(0 /* no expand */, 0); - layout->AddView(combobox_); - - if (combobox_model_.GetItemCount() > 0) { - layout->StartRow(1, 0); - example_shown_->SetLayoutManager(new FillLayout()); - example_shown_->AddChildView(combobox_model_.GetItemViewAt(0)); - layout->AddView(example_shown_); - } - - layout->StartRow(0 /* no expand */, 0); - layout->AddView(status_label_); - layout->AddPaddingRow(0, 5); - } - - // Adds all the individual examples to the combobox model. - void AddExamples() { - // Please keep this list in alphabetical order! - combobox_model_.AddExample(new BubbleExample); - combobox_model_.AddExample(new ButtonExample); - combobox_model_.AddExample(new CheckboxExample); - combobox_model_.AddExample(new ComboboxExample); - combobox_model_.AddExample(new DoubleSplitViewExample); - combobox_model_.AddExample(new LabelExample); - combobox_model_.AddExample(new LinkExample); - combobox_model_.AddExample(new MenuExample); - combobox_model_.AddExample(new MessageBoxExample); - combobox_model_.AddExample(new MultilineExample); - combobox_model_.AddExample(new ProgressBarExample); - combobox_model_.AddExample(new RadioButtonExample); - combobox_model_.AddExample(new ScrollViewExample); - combobox_model_.AddExample(new SingleSplitViewExample); - combobox_model_.AddExample(new SliderExample); - combobox_model_.AddExample(new TabbedPaneExample); - combobox_model_.AddExample(new TableExample); - combobox_model_.AddExample(new TextExample); - combobox_model_.AddExample(new TextfieldExample); - combobox_model_.AddExample(new ThrobberExample); - combobox_model_.AddExample(new TreeViewExample); - combobox_model_.AddExample(new WebViewExample(browser_context_)); - combobox_model_.AddExample(new WidgetExample); - } - - static ExamplesWindowContents* instance_; - ComboboxModelExampleList combobox_model_; - Combobox* combobox_; - View* example_shown_; - Label* status_label_; - const Operation operation_; - content::BrowserContext* browser_context_; - - DISALLOW_COPY_AND_ASSIGN(ExamplesWindowContents); -}; - -// static -ExamplesWindowContents* ExamplesWindowContents::instance_ = NULL; - void ShowExamplesWindowWithContent(Operation operation, content::BrowserContext* browser_context, gfx::NativeView window_context) { - if (ExamplesWindowContents::instance()) { - ExamplesWindowContents::instance()->GetWidget()->Activate(); - } else { - Widget* widget = new Widget; - Widget::InitParams params; - params.delegate = new ExamplesWindowContents(operation, browser_context); - params.context = window_context; - params.bounds = gfx::Rect(0, 0, 850, 300); - params.top_level = true; - widget->Init(params); - widget->Show(); - } -} - -void LogStatus(const std::string& string) { - ExamplesWindowContents::instance()->SetStatus(string); + scoped_ptr<ScopedVector<ExampleBase> > extra_examples( + new ScopedVector<ExampleBase>); + extra_examples->push_back(new WebViewExample(browser_context)); + ShowExamplesWindow(operation, window_context, extra_examples.Pass()); } } // namespace examples diff --git a/chromium/ui/views/examples/examples_window_with_content.h b/chromium/ui/views/examples/examples_window_with_content.h index 907d3075a28..407fd2eeb7e 100644 --- a/chromium/ui/views/examples/examples_window_with_content.h +++ b/chromium/ui/views/examples/examples_window_with_content.h @@ -6,6 +6,7 @@ #define UI_VIEWS_EXAMPLES_EXAMPLES_WINDOW_WITH_CONTENT_H_ #include "ui/gfx/native_widget_types.h" +#include "ui/views/examples/examples_window.h" #include "ui/views/examples/views_examples_with_content_export.h" namespace content { @@ -15,11 +16,6 @@ class BrowserContext; namespace views { namespace examples { -enum Operation { - DO_NOTHING_ON_CLOSE = 0, - QUIT_ON_CLOSE, -}; - // Shows a window with the views examples in it. VIEWS_EXAMPLES_WITH_CONTENT_EXPORT void ShowExamplesWindowWithContent( Operation operation, diff --git a/chromium/ui/views/examples/examples_with_content_main_exe.cc b/chromium/ui/views/examples/examples_with_content_main_exe.cc new file mode 100644 index 00000000000..8df1dec30ab --- /dev/null +++ b/chromium/ui/views/examples/examples_with_content_main_exe.cc @@ -0,0 +1,49 @@ +// Copyright 2014 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/bind.h" +#include "content/public/browser/browser_context.h" +#include "ui/views/examples/examples_window_with_content.h" +#include "ui/views_content_client/views_content_client.h" + +#if defined(OS_WIN) +#include "content/public/app/startup_helper_win.h" +#include "sandbox/win/src/sandbox_types.h" +#endif + +namespace { + +void ShowContentExampleWindow(content::BrowserContext* browser_context, + gfx::NativeView window_context) { + views::examples::ShowExamplesWindowWithContent(views::examples::QUIT_ON_CLOSE, + browser_context, + window_context); + + // These lines serve no purpose other than to introduce an explicit content + // dependency. If the main executable doesn't have this dependency, the linker + // has more flexibility to reorder library dependencies in a shared component + // build. On linux, this can cause libc to appear before libcontent in the + // dlsym search path, which breaks (usually valid) assumptions made in + // sandbox::InitLibcUrandomOverrides(). See http://crbug.com/374712. + if (!browser_context) { + content::BrowserContext::SaveSessionState(NULL); + NOTREACHED(); + } +} + +} // namespace + +#if defined(OS_WIN) +int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t*, int) { + sandbox::SandboxInterfaceInfo sandbox_info = {0}; + content::InitializeSandboxInfo(&sandbox_info); + ui::ViewsContentClient views_content_client(instance, &sandbox_info); +#else +int main(int argc, const char** argv) { + ui::ViewsContentClient views_content_client(argc, argv); +#endif + + views_content_client.set_task(base::Bind(&ShowContentExampleWindow)); + return views_content_client.RunMain(); +} diff --git a/chromium/ui/views/examples/label_example.cc b/chromium/ui/views/examples/label_example.cc index d970bf1a2d5..4dea806e4d4 100644 --- a/chromium/ui/views/examples/label_example.cc +++ b/chromium/ui/views/examples/label_example.cc @@ -10,6 +10,9 @@ #include "ui/views/layout/box_layout.h" #include "ui/views/view.h" +using base::ASCIIToUTF16; +using base::WideToUTF16; + namespace views { namespace examples { @@ -18,24 +21,20 @@ namespace { // A Label with a constrained preferred size to demonstrate eliding or wrapping. class PreferredSizeLabel : public Label { public: - PreferredSizeLabel(); - virtual ~PreferredSizeLabel(); + PreferredSizeLabel() : Label() { + SetBorder(Border::CreateSolidBorder(2, SK_ColorCYAN)); + } + virtual ~PreferredSizeLabel() {} - // Overridden from Label: - virtual gfx::Size GetPreferredSize() OVERRIDE; + // Label: + virtual gfx::Size GetPreferredSize() const OVERRIDE { + return gfx::Size(100, 40); + } private: DISALLOW_COPY_AND_ASSIGN(PreferredSizeLabel); }; -PreferredSizeLabel::PreferredSizeLabel() : Label() { - set_border(Border::CreateSolidBorder(2, SK_ColorCYAN)); -} - -PreferredSizeLabel::~PreferredSizeLabel() {} - -gfx::Size PreferredSizeLabel::GetPreferredSize() { return gfx::Size(100, 40); } - } // namespace LabelExample::LabelExample() : ExampleBase("Label") {} @@ -63,10 +62,12 @@ void LabelExample::CreateExampleView(View* container) { label->SetEnabledColor(SK_ColorBLUE); container->AddChildView(label); - label = new Label(ASCIIToUTF16("A Courier-18 label with a shadow.")); - label->SetFont(gfx::Font("Courier", 18)); - label->SetShadowColors(SK_ColorGRAY, SK_ColorLTGRAY); - label->SetShadowOffset(1, 1); + label = new Label(ASCIIToUTF16("A Courier-18 label with shadows.")); + label->SetFontList(gfx::FontList("Courier, 18px")); + gfx::ShadowValues shadows(1, gfx::ShadowValue(gfx::Point(), 1, SK_ColorRED)); + gfx::ShadowValue shadow(gfx::Point(2, 2), 0, SK_ColorGRAY); + shadows.push_back(shadow); + label->set_shadows(shadows); container->AddChildView(label); label = new PreferredSizeLabel(); @@ -75,10 +76,20 @@ void LabelExample::CreateExampleView(View* container) { container->AddChildView(label); label = new PreferredSizeLabel(); + label->SetElideBehavior(gfx::FADE_TAIL); + label->SetText(ASCIIToUTF16("Some long labels will fade, rather than elide, " + "if the text's width exceeds the label's available width.")); + container->AddChildView(label); + + label = new PreferredSizeLabel(); label->SetText(ASCIIToUTF16("A multi-line label will wrap onto subsequent " "lines if the text's width exceeds the label's available width.")); label->SetMultiLine(true); container->AddChildView(label); + + label = new Label(WideToUTF16(L"Password!")); + label->SetObscured(true); + container->AddChildView(label); } } // namespace examples diff --git a/chromium/ui/views/examples/label_example.h b/chromium/ui/views/examples/label_example.h index 0f7eeae92f1..8b7cfe782f5 100644 --- a/chromium/ui/views/examples/label_example.h +++ b/chromium/ui/views/examples/label_example.h @@ -5,19 +5,18 @@ #ifndef UI_VIEWS_EXAMPLES_LABEL_EXAMPLE_H_ #define UI_VIEWS_EXAMPLES_LABEL_EXAMPLE_H_ -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "base/macros.h" #include "ui/views/examples/example_base.h" namespace views { namespace examples { -class LabelExample : public ExampleBase { +class VIEWS_EXAMPLES_EXPORT LabelExample : public ExampleBase { public: LabelExample(); virtual ~LabelExample(); - // Overridden from ExampleBase: + // ExampleBase: virtual void CreateExampleView(View* container) OVERRIDE; private: diff --git a/chromium/ui/views/examples/link_example.cc b/chromium/ui/views/examples/link_example.cc index a28c5d579e5..3795c8c9832 100644 --- a/chromium/ui/views/examples/link_example.cc +++ b/chromium/ui/views/examples/link_example.cc @@ -19,7 +19,7 @@ LinkExample::~LinkExample() { } void LinkExample::CreateExampleView(View* container) { - link_ = new Link(ASCIIToUTF16("Click me!")); + link_ = new Link(base::ASCIIToUTF16("Click me!")); link_->set_listener(this); container->SetLayoutManager(new FillLayout); diff --git a/chromium/ui/views/examples/link_example.h b/chromium/ui/views/examples/link_example.h index ac969c43a0c..fd6e9245318 100644 --- a/chromium/ui/views/examples/link_example.h +++ b/chromium/ui/views/examples/link_example.h @@ -5,24 +5,24 @@ #ifndef UI_VIEWS_EXAMPLES_LINK_EXAMPLE_H_ #define UI_VIEWS_EXAMPLES_LINK_EXAMPLE_H_ -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "base/macros.h" #include "ui/views/controls/link_listener.h" #include "ui/views/examples/example_base.h" namespace views { namespace examples { -class LinkExample : public ExampleBase, public LinkListener { +class VIEWS_EXAMPLES_EXPORT LinkExample : public ExampleBase, + public LinkListener { public: LinkExample(); virtual ~LinkExample(); - // Overridden from ExampleBase: + // ExampleBase: virtual void CreateExampleView(View* container) OVERRIDE; private: - // Overridden from LinkListener: + // LinkListener: virtual void LinkClicked(Link* source, int event_flags) OVERRIDE; Link* link_; diff --git a/chromium/ui/views/examples/menu_example.cc b/chromium/ui/views/examples/menu_example.cc index 307a6ca794e..742cfffa4b2 100644 --- a/chromium/ui/views/examples/menu_example.cc +++ b/chromium/ui/views/examples/menu_example.cc @@ -15,6 +15,8 @@ #include "ui/views/view.h" #include "ui/views/widget/widget.h" +using base::ASCIIToUTF16; + namespace views { namespace examples { @@ -25,7 +27,7 @@ class ExampleMenuModel : public ui::SimpleMenuModel, public: ExampleMenuModel(); - // Overridden from ui::SimpleMenuModel::Delegate: + // ui::SimpleMenuModel::Delegate: virtual bool IsCommandIdChecked(int command_id) const OVERRIDE; virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE; virtual bool GetAcceleratorForCommandId( @@ -58,11 +60,11 @@ class ExampleMenuModel : public ui::SimpleMenuModel, class ExampleMenuButton : public MenuButton, public MenuButtonListener { public: - explicit ExampleMenuButton(const string16& test); + explicit ExampleMenuButton(const base::string16& test); virtual ~ExampleMenuButton(); private: - // Overridden from MenuButtonListener: + // MenuButtonListener: virtual void OnMenuButtonClicked(View* source, const gfx::Point& point) OVERRIDE; @@ -126,24 +128,24 @@ bool ExampleMenuModel::GetAcceleratorForCommandId( void ExampleMenuModel::ExecuteCommand(int command_id, int event_flags) { switch (command_id) { case COMMAND_DO_SOMETHING: { - LOG(INFO) << "Done something"; + VLOG(0) << "Done something"; break; } // Radio items. case COMMAND_SELECT_ASCII: { current_encoding_command_id_ = COMMAND_SELECT_ASCII; - LOG(INFO) << "Selected ASCII"; + VLOG(0) << "Selected ASCII"; break; } case COMMAND_SELECT_UTF8: { current_encoding_command_id_ = COMMAND_SELECT_UTF8; - LOG(INFO) << "Selected UTF-8"; + VLOG(0) << "Selected UTF-8"; break; } case COMMAND_SELECT_UTF16: { current_encoding_command_id_ = COMMAND_SELECT_UTF16; - LOG(INFO) << "Selected UTF-16"; + VLOG(0) << "Selected UTF-16"; break; } @@ -176,7 +178,7 @@ void ExampleMenuModel::ExecuteCommand(int command_id, int event_flags) { // ExampleMenuButton ----------------------------------------------------------- -ExampleMenuButton::ExampleMenuButton(const string16& test) +ExampleMenuButton::ExampleMenuButton(const base::string16& test) : MenuButton(NULL, test, this, true) { } @@ -187,11 +189,15 @@ void ExampleMenuButton::OnMenuButtonClicked(View* source, const gfx::Point& point) { menu_runner_.reset(new MenuRunner(GetMenuModel())); - if (menu_runner_->RunMenuAt(source->GetWidget()->GetTopLevelWidget(), this, - gfx::Rect(point, gfx::Size()), MenuItemView::TOPRIGHT, - ui::MENU_SOURCE_NONE, MenuRunner::HAS_MNEMONICS) == - MenuRunner::MENU_DELETED) + if (menu_runner_->RunMenuAt(source->GetWidget()->GetTopLevelWidget(), + this, + gfx::Rect(point, gfx::Size()), + MENU_ANCHOR_TOPRIGHT, + ui::MENU_SOURCE_NONE, + MenuRunner::HAS_MNEMONICS) == + MenuRunner::MENU_DELETED) { return; + } } ui::SimpleMenuModel* ExampleMenuButton::GetMenuModel() { diff --git a/chromium/ui/views/examples/menu_example.h b/chromium/ui/views/examples/menu_example.h index 40c9b23d6e0..3ed5d37e7a4 100644 --- a/chromium/ui/views/examples/menu_example.h +++ b/chromium/ui/views/examples/menu_example.h @@ -5,8 +5,7 @@ #ifndef UI_VIEWS_EXAMPLES_MENU_EXAMPLE_H_ #define UI_VIEWS_EXAMPLES_MENU_EXAMPLE_H_ -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "base/macros.h" #include "ui/views/examples/example_base.h" namespace views { @@ -14,12 +13,12 @@ namespace examples { // MenuExample demonstrates how to use the MenuModelAdapter and MenuRunner // classes. -class MenuExample : public ExampleBase { +class VIEWS_EXAMPLES_EXPORT MenuExample : public ExampleBase { public: MenuExample(); virtual ~MenuExample(); - // Overridden from ExampleBase: + // ExampleBase: virtual void CreateExampleView(View* container) OVERRIDE; private: diff --git a/chromium/ui/views/examples/message_box_example.cc b/chromium/ui/views/examples/message_box_example.cc index 2563525f325..cb9e924fbf6 100644 --- a/chromium/ui/views/examples/message_box_example.cc +++ b/chromium/ui/views/examples/message_box_example.cc @@ -10,6 +10,8 @@ #include "ui/views/layout/grid_layout.h" #include "ui/views/view.h" +using base::ASCIIToUTF16; + namespace views { namespace examples { diff --git a/chromium/ui/views/examples/message_box_example.h b/chromium/ui/views/examples/message_box_example.h index 4d5277805eb..692ae22255a 100644 --- a/chromium/ui/views/examples/message_box_example.h +++ b/chromium/ui/views/examples/message_box_example.h @@ -7,8 +7,7 @@ #include <string> -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "base/macros.h" #include "ui/views/controls/button/button.h" #include "ui/views/examples/example_base.h" @@ -18,16 +17,17 @@ class MessageBoxView; namespace examples { // A MessageBoxView example. This tests some of checkbox features as well. -class MessageBoxExample : public ExampleBase, public ButtonListener { +class VIEWS_EXAMPLES_EXPORT MessageBoxExample : public ExampleBase, + public ButtonListener { public: MessageBoxExample(); virtual ~MessageBoxExample(); - // Overridden from ExampleBase: + // ExampleBase: virtual void CreateExampleView(View* container) OVERRIDE; private: - // Overridden from ButtonListener: + // ButtonListener: virtual void ButtonPressed(Button* sender, const ui::Event& event) OVERRIDE; // The MessageBoxView to be tested. diff --git a/chromium/ui/views/examples/multiline_example.cc b/chromium/ui/views/examples/multiline_example.cc index b1923846386..e0a3b152b54 100644 --- a/chromium/ui/views/examples/multiline_example.cc +++ b/chromium/ui/views/examples/multiline_example.cc @@ -9,14 +9,27 @@ #include "ui/gfx/render_text.h" #include "ui/views/background.h" #include "ui/views/border.h" +#include "ui/views/controls/button/checkbox.h" #include "ui/views/controls/label.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/layout/grid_layout.h" #include "ui/views/view.h" +using base::ASCIIToUTF16; + namespace views { namespace examples { +namespace { + +gfx::Range ClampRange(gfx::Range range, size_t max) { + range.set_start(std::min(range.start(), max)); + range.set_end(std::min(range.end(), max)); + return range; +} + +} // namespace + // A simple View that hosts a RenderText object. class MultilineExample::RenderTextView : public View { public: @@ -24,7 +37,7 @@ class MultilineExample::RenderTextView : public View { render_text_->SetHorizontalAlignment(gfx::ALIGN_CENTER); render_text_->SetColor(SK_ColorBLACK); render_text_->SetMultiline(true); - set_border(Border::CreateSolidBorder(2, SK_ColorGRAY)); + SetBorder(Border::CreateSolidBorder(2, SK_ColorGRAY)); } virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { @@ -32,7 +45,7 @@ class MultilineExample::RenderTextView : public View { render_text_->Draw(canvas); } - virtual gfx::Size GetPreferredSize() OVERRIDE { + virtual gfx::Size GetPreferredSize() const OVERRIDE { // Turn off multiline mode to get the single-line text size, which is the // preferred size for this view. render_text_->SetMultiline(false); @@ -43,7 +56,7 @@ class MultilineExample::RenderTextView : public View { return size; } - virtual int GetHeightForWidth(int w) OVERRIDE { + virtual int GetHeightForWidth(int w) const OVERRIDE { // TODO(ckocagil): Why does this happen? if (w == 0) return View::GetHeightForWidth(w); @@ -56,19 +69,22 @@ class MultilineExample::RenderTextView : public View { return height; } - void SetText(const string16& new_contents) { + void SetText(const base::string16& new_contents) { // Color and style the text inside |test_range| to test colors and styles. - gfx::Range test_range(1, 21); - test_range.set_start(std::min(test_range.start(), new_contents.length())); - test_range.set_end(std::min(test_range.end(), new_contents.length())); + const size_t range_max = new_contents.length(); + gfx::Range color_range = ClampRange(gfx::Range(1, 21), range_max); + gfx::Range bold_range = ClampRange(gfx::Range(4, 10), range_max); + gfx::Range italic_range = ClampRange(gfx::Range(7, 13), range_max); render_text_->SetText(new_contents); render_text_->SetColor(SK_ColorBLACK); - render_text_->ApplyColor(0xFFFF0000, test_range); + render_text_->ApplyColor(0xFFFF0000, color_range); render_text_->SetStyle(gfx::DIAGONAL_STRIKE, false); - render_text_->ApplyStyle(gfx::DIAGONAL_STRIKE, true, test_range); + render_text_->ApplyStyle(gfx::DIAGONAL_STRIKE, true, color_range); render_text_->SetStyle(gfx::UNDERLINE, false); - render_text_->ApplyStyle(gfx::UNDERLINE, true, test_range); + render_text_->ApplyStyle(gfx::UNDERLINE, true, color_range); + render_text_->ApplyStyle(gfx::BOLD, true, bold_range); + render_text_->ApplyStyle(gfx::ITALIC, true, italic_range); InvalidateLayout(); } @@ -96,16 +112,17 @@ MultilineExample::~MultilineExample() { } void MultilineExample::CreateExampleView(View* container) { - const char kTestString[] = "test string asdf 1234 test string asdf 1234 " - "test string asdf 1234 test string asdf 1234"; + const base::string16 kTestString = base::WideToUTF16(L"qwerty" + L"\x627\x644\x631\x626\x64A\x633\x64A\x629" + L"asdfgh"); render_text_view_ = new RenderTextView(); - render_text_view_->SetText(ASCIIToUTF16(kTestString)); + render_text_view_->SetText(kTestString); label_ = new Label(); - label_->SetText(ASCIIToUTF16(kTestString)); + label_->SetText(kTestString); label_->SetMultiLine(true); - label_->set_border(Border::CreateSolidBorder(2, SK_ColorCYAN)); + label_->SetBorder(Border::CreateSolidBorder(2, SK_ColorCYAN)); label_checkbox_ = new Checkbox(ASCIIToUTF16("views::Label:")); label_checkbox_->SetChecked(true); @@ -113,8 +130,8 @@ void MultilineExample::CreateExampleView(View* container) { label_checkbox_->set_request_focus_on_press(false); textfield_ = new Textfield(); - textfield_->SetController(this); - textfield_->SetText(ASCIIToUTF16(kTestString)); + textfield_->set_controller(this); + textfield_->SetText(kTestString); GridLayout* layout = new GridLayout(container); container->SetLayoutManager(layout); @@ -139,7 +156,7 @@ void MultilineExample::CreateExampleView(View* container) { } void MultilineExample::ContentsChanged(Textfield* sender, - const string16& new_contents) { + const base::string16& new_contents) { render_text_view_->SetText(new_contents); if (label_checkbox_->checked()) label_->SetText(new_contents); @@ -154,7 +171,8 @@ bool MultilineExample::HandleKeyEvent(Textfield* sender, void MultilineExample::ButtonPressed(Button* sender, const ui::Event& event) { DCHECK_EQ(sender, label_checkbox_); - label_->SetText(label_checkbox_->checked() ? textfield_->text() : string16()); + label_->SetText(label_checkbox_->checked() ? textfield_->text() : + base::string16()); container()->Layout(); container()->SchedulePaint(); } diff --git a/chromium/ui/views/examples/multiline_example.h b/chromium/ui/views/examples/multiline_example.h index 2757c4ac933..7231c66bce7 100644 --- a/chromium/ui/views/examples/multiline_example.h +++ b/chromium/ui/views/examples/multiline_example.h @@ -5,22 +5,22 @@ #ifndef UI_VIEWS_EXAMPLES_MULTILINE_EXAMPLE_H_ #define UI_VIEWS_EXAMPLES_MULTILINE_EXAMPLE_H_ -#include "base/compiler_specific.h" -#include "base/strings/string16.h" -#include "ui/views/controls/button/checkbox.h" +#include "base/macros.h" +#include "ui/views/controls/button/button.h" #include "ui/views/controls/textfield/textfield_controller.h" #include "ui/views/examples/example_base.h" namespace views { +class Checkbox; class Label; namespace examples { // An example that compares the multiline rendering of different controls. -class MultilineExample : public ExampleBase, - public TextfieldController, - public ButtonListener { +class VIEWS_EXAMPLES_EXPORT MultilineExample : public ExampleBase, + public TextfieldController, + public ButtonListener { public: MultilineExample(); virtual ~MultilineExample(); @@ -36,7 +36,7 @@ class MultilineExample : public ExampleBase, // TextfieldController: virtual void ContentsChanged(Textfield* sender, - const string16& new_contents) OVERRIDE; + const base::string16& new_contents) OVERRIDE; virtual bool HandleKeyEvent(Textfield* sender, const ui::KeyEvent& key_event) OVERRIDE; diff --git a/chromium/ui/views/examples/progress_bar_example.cc b/chromium/ui/views/examples/progress_bar_example.cc index ce29ecd0fa2..8a860f212af 100644 --- a/chromium/ui/views/examples/progress_bar_example.cc +++ b/chromium/ui/views/examples/progress_bar_example.cc @@ -51,11 +51,11 @@ void ProgressBarExample::CreateExampleView(View* container) { 0, GridLayout::USE_PREF, 0, 0); layout->StartRow(0, 0); - minus_button_ = new LabelButton(this, ASCIIToUTF16("-")); + minus_button_ = new LabelButton(this, base::ASCIIToUTF16("-")); layout->AddView(minus_button_); progress_bar_ = new ProgressBar(); layout->AddView(progress_bar_); - plus_button_ = new LabelButton(this, ASCIIToUTF16("+")); + plus_button_ = new LabelButton(this, base::ASCIIToUTF16("+")); layout->AddView(plus_button_); } diff --git a/chromium/ui/views/examples/progress_bar_example.h b/chromium/ui/views/examples/progress_bar_example.h index 92d0e9f1670..8533b7ac3cb 100644 --- a/chromium/ui/views/examples/progress_bar_example.h +++ b/chromium/ui/views/examples/progress_bar_example.h @@ -5,8 +5,7 @@ #ifndef UI_VIEWS_EXAMPLES_PROGRESS_BAR_EXAMPLE_H_ #define UI_VIEWS_EXAMPLES_PROGRESS_BAR_EXAMPLE_H_ -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "base/macros.h" #include "ui/views/controls/button/button.h" #include "ui/views/examples/example_base.h" @@ -15,16 +14,17 @@ class ProgressBar; namespace examples { -class ProgressBarExample : public ExampleBase, public ButtonListener { +class VIEWS_EXAMPLES_EXPORT ProgressBarExample : public ExampleBase, + public ButtonListener { public: ProgressBarExample(); virtual ~ProgressBarExample(); - // Overridden from ExampleBase: + // ExampleBase: virtual void CreateExampleView(View* container) OVERRIDE; private: - // Overridden from ButtonListener: + // ButtonListener: virtual void ButtonPressed(Button* button, const ui::Event& event) OVERRIDE; Button* minus_button_; diff --git a/chromium/ui/views/examples/radio_button_example.cc b/chromium/ui/views/examples/radio_button_example.cc index 72a844e0f5f..5e3c7dd4a3c 100644 --- a/chromium/ui/views/examples/radio_button_example.cc +++ b/chromium/ui/views/examples/radio_button_example.cc @@ -23,13 +23,13 @@ RadioButtonExample::~RadioButtonExample() { } void RadioButtonExample::CreateExampleView(View* container) { - select_ = new LabelButton(this, ASCIIToUTF16("Select")); - status_ = new LabelButton(this, ASCIIToUTF16("Show Status")); + select_ = new LabelButton(this, base::ASCIIToUTF16("Select")); + status_ = new LabelButton(this, base::ASCIIToUTF16("Show Status")); int group = 1; for (size_t i = 0; i < arraysize(radio_buttons_); ++i) { radio_buttons_[i] = new RadioButton( - UTF8ToUTF16(base::StringPrintf( + base::UTF8ToUTF16(base::StringPrintf( "Radio %d in group %d", static_cast<int>(i) + 1, group)), group); radio_buttons_[i]->set_listener(this); diff --git a/chromium/ui/views/examples/radio_button_example.h b/chromium/ui/views/examples/radio_button_example.h index ed8ddf4cf99..cc1452215e0 100644 --- a/chromium/ui/views/examples/radio_button_example.h +++ b/chromium/ui/views/examples/radio_button_example.h @@ -7,8 +7,7 @@ #include <string> -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "base/macros.h" #include "ui/views/controls/button/button.h" #include "ui/views/examples/example_base.h" @@ -19,16 +18,17 @@ class RadioButton; namespace examples { -class RadioButtonExample : public ExampleBase, public ButtonListener { +class VIEWS_EXAMPLES_EXPORT RadioButtonExample : public ExampleBase, + public ButtonListener { public: RadioButtonExample(); virtual ~RadioButtonExample(); - // Overridden from ExampleBase: + // ExampleBase: virtual void CreateExampleView(View* container) OVERRIDE; private: - // Overridden from ButtonListener: + // ButtonListener: virtual void ButtonPressed(Button* sender, const ui::Event& event) OVERRIDE; // Group of 3 radio buttons. diff --git a/chromium/ui/views/examples/scroll_view_example.cc b/chromium/ui/views/examples/scroll_view_example.cc index 38b18fc5e7f..127442fb7df 100644 --- a/chromium/ui/views/examples/scroll_view_example.cc +++ b/chromium/ui/views/examples/scroll_view_example.cc @@ -12,6 +12,8 @@ #include "ui/views/layout/grid_layout.h" #include "ui/views/view.h" +using base::ASCIIToUTF16; + namespace views { namespace examples { @@ -25,7 +27,7 @@ class ScrollViewExample::ScrollableView : public View { AddChildView(new RadioButton(ASCIIToUTF16("Radio Button"), 0)); } - virtual gfx::Size GetPreferredSize() OVERRIDE { + virtual gfx::Size GetPreferredSize() const OVERRIDE { return gfx::Size(width(), height()); } diff --git a/chromium/ui/views/examples/scroll_view_example.h b/chromium/ui/views/examples/scroll_view_example.h index e180bc0b4b5..d850b01df4e 100644 --- a/chromium/ui/views/examples/scroll_view_example.h +++ b/chromium/ui/views/examples/scroll_view_example.h @@ -7,8 +7,7 @@ #include <string> -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "base/macros.h" #include "ui/views/controls/button/button.h" #include "ui/views/controls/scroll_view.h" #include "ui/views/examples/example_base.h" @@ -19,16 +18,17 @@ class LabelButton; namespace examples { -class ScrollViewExample : public ExampleBase, public ButtonListener { +class VIEWS_EXAMPLES_EXPORT ScrollViewExample : public ExampleBase, + public ButtonListener { public: ScrollViewExample(); virtual ~ScrollViewExample(); - // Overridden from ExampleBase: + // ExampleBase: virtual void CreateExampleView(View* container) OVERRIDE; private: - // Overridden from ButtonListener: + // ButtonListener: virtual void ButtonPressed(Button* sender, const ui::Event& event) OVERRIDE; // Control buttons to change the size of scrollable and jump to diff --git a/chromium/ui/views/examples/single_split_view_example.cc b/chromium/ui/views/examples/single_split_view_example.cc index 97fb36b7980..7e390b51064 100644 --- a/chromium/ui/views/examples/single_split_view_example.cc +++ b/chromium/ui/views/examples/single_split_view_example.cc @@ -21,9 +21,9 @@ class SplittedView : public View { void SetColor(SkColor from, SkColor to); private: - // Overridden from View: - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual gfx::Size GetMinimumSize() OVERRIDE; + // View: + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual gfx::Size GetMinimumSize() const OVERRIDE; virtual void Layout() OVERRIDE; DISALLOW_COPY_AND_ASSIGN(SplittedView); @@ -40,11 +40,11 @@ void SplittedView::SetColor(SkColor from, SkColor to) { set_background(Background::CreateVerticalGradientBackground(from, to)); } -gfx::Size SplittedView::GetPreferredSize() { +gfx::Size SplittedView::GetPreferredSize() const { return gfx::Size(width(), height()); } -gfx::Size SplittedView::GetMinimumSize() { +gfx::Size SplittedView::GetMinimumSize() const { return gfx::Size(10, 10); } diff --git a/chromium/ui/views/examples/single_split_view_example.h b/chromium/ui/views/examples/single_split_view_example.h index 0c157cc87b9..603482c2288 100644 --- a/chromium/ui/views/examples/single_split_view_example.h +++ b/chromium/ui/views/examples/single_split_view_example.h @@ -5,24 +5,25 @@ #ifndef UI_VIEWS_EXAMPLES_SINGLE_SPLIT_VIEW_EXAMPLE_H_ #define UI_VIEWS_EXAMPLES_SINGLE_SPLIT_VIEW_EXAMPLE_H_ -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "base/macros.h" #include "ui/views/controls/single_split_view_listener.h" #include "ui/views/examples/example_base.h" namespace views { namespace examples { -class SingleSplitViewExample : public ExampleBase, - public SingleSplitViewListener { +class VIEWS_EXAMPLES_EXPORT SingleSplitViewExample + : public ExampleBase, + public SingleSplitViewListener { public: SingleSplitViewExample(); virtual ~SingleSplitViewExample(); - // Overridden from ExampleBase: + // ExampleBase: virtual void CreateExampleView(View* container) OVERRIDE; private: + // SingleSplitViewListener: virtual bool SplitHandleMoved(SingleSplitView* sender) OVERRIDE; SingleSplitView* single_split_view_; diff --git a/chromium/ui/views/examples/slider_example.cc b/chromium/ui/views/examples/slider_example.cc index 8ae4d6bc423..89f78568e23 100644 --- a/chromium/ui/views/examples/slider_example.cc +++ b/chromium/ui/views/examples/slider_example.cc @@ -37,7 +37,7 @@ void SliderExample::SliderValueChanged(Slider* sender, float value, float old_value, SliderChangeReason reason) { - label_->SetText(ASCIIToUTF16(base::StringPrintf("%.3lf", value))); + label_->SetText(base::ASCIIToUTF16(base::StringPrintf("%.3lf", value))); } } // namespace examples diff --git a/chromium/ui/views/examples/slider_example.h b/chromium/ui/views/examples/slider_example.h index 8e909394467..32950f9ad36 100644 --- a/chromium/ui/views/examples/slider_example.h +++ b/chromium/ui/views/examples/slider_example.h @@ -5,8 +5,7 @@ #ifndef UI_VIEWS_EXAMPLES_SLIDER_EXAMPLE_H_ #define UI_VIEWS_EXAMPLES_SLIDER_EXAMPLE_H_ -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "base/macros.h" #include "ui/views/controls/slider.h" #include "ui/views/examples/example_base.h" @@ -15,16 +14,17 @@ class Label; namespace examples { -class SliderExample : public ExampleBase, public SliderListener { +class VIEWS_EXAMPLES_EXPORT SliderExample : public ExampleBase, + public SliderListener { public: SliderExample(); virtual ~SliderExample(); - // Overridden from ExampleBase: + // ExampleBase: virtual void CreateExampleView(View* container) OVERRIDE; private: - // Overridden from SliderListener: + // SliderListener: virtual void SliderValueChanged(Slider* sender, float value, float old_value, diff --git a/chromium/ui/views/examples/tabbed_pane_example.cc b/chromium/ui/views/examples/tabbed_pane_example.cc index f0f6691ef87..087c77bf0df 100644 --- a/chromium/ui/views/examples/tabbed_pane_example.cc +++ b/chromium/ui/views/examples/tabbed_pane_example.cc @@ -9,6 +9,8 @@ #include "ui/views/controls/tabbed_pane/tabbed_pane.h" #include "ui/views/layout/grid_layout.h" +using base::ASCIIToUTF16; + namespace views { namespace examples { @@ -57,7 +59,7 @@ void TabbedPaneExample::ButtonPressed(Button* sender, const ui::Event& event) { if (sender == add_) { AddButton("Added"); } else if (sender == add_at_) { - const string16 label = ASCIIToUTF16("Added at 1"); + const base::string16 label = ASCIIToUTF16("Added at 1"); tabbed_pane_->AddTabAtIndex(1, label, new LabelButton(NULL, label)); } else if (sender == select_at_) { if (tabbed_pane_->GetTabCount() > 1) diff --git a/chromium/ui/views/examples/tabbed_pane_example.h b/chromium/ui/views/examples/tabbed_pane_example.h index 9a198269294..f23f6b34d7f 100644 --- a/chromium/ui/views/examples/tabbed_pane_example.h +++ b/chromium/ui/views/examples/tabbed_pane_example.h @@ -7,8 +7,7 @@ #include <string> -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "base/macros.h" #include "ui/views/controls/button/button.h" #include "ui/views/controls/tabbed_pane/tabbed_pane_listener.h" #include "ui/views/examples/example_base.h" @@ -19,9 +18,9 @@ class TabbedPane; namespace examples { // A TabbedPane example tests adding and selecting tabs. -class TabbedPaneExample : public ExampleBase, - public ButtonListener, - public TabbedPaneListener { +class VIEWS_EXAMPLES_EXPORT TabbedPaneExample : public ExampleBase, + public ButtonListener, + public TabbedPaneListener { public: TabbedPaneExample(); virtual ~TabbedPaneExample(); diff --git a/chromium/ui/views/examples/table_example.cc b/chromium/ui/views/examples/table_example.cc index 87032c7160a..5c9eebfd2ed 100644 --- a/chromium/ui/views/examples/table_example.cc +++ b/chromium/ui/views/examples/table_example.cc @@ -14,6 +14,8 @@ #include "ui/views/controls/button/checkbox.h" #include "ui/views/layout/grid_layout.h" +using base::ASCIIToUTF16; + namespace views { namespace examples { @@ -107,9 +109,9 @@ int TableExample::RowCount() { return 10; } -string16 TableExample::GetText(int row, int column_id) { +base::string16 TableExample::GetText(int row, int column_id) { if (row == -1) - return string16(); + return base::string16(); const char* const cells[5][4] = { { "Orange", "Orange", "South america", "$5" }, @@ -143,14 +145,14 @@ void TableExample::GetGroupRange(int model_index, GroupRange* range) { void TableExample::OnSelectionChanged() { PrintStatus("Selected: %s", - UTF16ToASCII(GetText(table_->selection_model().active(), - 0)).c_str()); + base::UTF16ToASCII(GetText(table_->selection_model().active(), + 0)).c_str()); } void TableExample::OnDoubleClick() { PrintStatus("Double Click: %s", - UTF16ToASCII(GetText(table_->selection_model().active(), - 0)).c_str()); + base::UTF16ToASCII(GetText(table_->selection_model().active(), + 0)).c_str()); } void TableExample::OnMiddleClick() {} diff --git a/chromium/ui/views/examples/table_example.h b/chromium/ui/views/examples/table_example.h index d715a107a2b..06ac3c01a68 100644 --- a/chromium/ui/views/examples/table_example.h +++ b/chromium/ui/views/examples/table_example.h @@ -7,8 +7,7 @@ #include <string> -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "base/macros.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/models/table_model.h" #include "ui/views/controls/button/button.h" @@ -27,11 +26,11 @@ class TableView; namespace examples { -class TableExample : public ExampleBase, - public ui::TableModel, - public TableGrouper, - public TableViewObserver, - public ButtonListener { +class VIEWS_EXAMPLES_EXPORT TableExample : public ExampleBase, + public ui::TableModel, + public TableGrouper, + public TableViewObserver, + public ButtonListener { public: TableExample(); virtual ~TableExample(); @@ -41,7 +40,7 @@ class TableExample : public ExampleBase, // ui::TableModel: virtual int RowCount() OVERRIDE; - virtual string16 GetText(int row, int column_id) OVERRIDE; + virtual base::string16 GetText(int row, int column_id) OVERRIDE; virtual gfx::ImageSkia GetIcon(int row) OVERRIDE; virtual void SetObserver(ui::TableModelObserver* observer) OVERRIDE; diff --git a/chromium/ui/views/examples/text_example.cc b/chromium/ui/views/examples/text_example.cc index ec43ac15b99..2682e73bd14 100644 --- a/chromium/ui/views/examples/text_example.cc +++ b/chromium/ui/views/examples/text_example.cc @@ -24,48 +24,31 @@ namespace { // Number of columns in the view layout. const int kNumColumns = 10; -const char kShortText[] = "Batman"; -const char kMediumText[] = "The quick brown fox jumps over the lazy dog."; +const char kShortText[] = "The quick brown fox jumps over the lazy dog."; const char kLongText[] = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod " "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim " "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea " - "commodo consequat. Duis aute irure dolor in reprehenderit in voluptate " - "velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint " + "commodo consequat.\nDuis aute irure dolor in reprehenderit in voluptate " + "velit esse cillum dolore eu fugiat nulla pariatur.\n\nExcepteur sint " "occaecat cupidatat non proident, sunt in culpa qui officia deserunt " "mollit anim id est laborum."; const char kAmpersandText[] = "The quick && &brown fo&x jumps over the lazy dog."; -const char kNewlineText[] = - "The quick \nbrown fox jumps\n\n over the lazy dog."; - -const char* kTextExamples[] = { - "Short", - "Medium", - "Long", - "Ampersands", - "Newlines", -}; - -const char* kElidingBehaviors[] = { - "Ellipsis", - "None", - "Fade Tail", - "Fade Head", -}; - -const char* kPrefixOptions[] = { - "Default", - "Show", - "Hide", -}; - -const char* kHorizontalAligments[] = { - "Default", - "Left", - "Center", - "Right", -}; +const wchar_t kRightToLeftText[] = + L"\x5e9\x5dc\x5d5\x5dd \x5d4\x5e2\x5d5\x5dc\x5dd! " + L"\x5e9\x5dc\x5d5\x5dd \x5d4\x5e2\x5d5\x5dc\x5dd! " + L"\x5e9\x5dc\x5d5\x5dd \x5d4\x5e2\x5d5\x5dc\x5dd! " + L"\x5e9\x5dc\x5d5\x5dd \x5d4\x5e2\x5d5\x5dc\x5dd! " + L"\x5e9\x5dc\x5d5\x5dd \x5d4\x5e2\x5d5\x5dc\x5dd! " + L"\x5e9\x5dc\x5d5\x5dd \x5d4\x5e2\x5d5\x5dc\x5dd! " + L"\x5e9\x5dc\x5d5\x5dd \x5d4\x5e2\x5d5\x5dc\x5dd! " + L"\x5e9\x5dc\x5d5\x5dd \x5d4\x5e2\x5d5\x5dc\x5dd!"; + +const char* kTextExamples[] = { "Short", "Long", "Ampersands", "RTL Hebrew", }; +const char* kElideBehaviors[] = { "Elide", "Truncate", "Fade", }; +const char* kPrefixOptions[] = { "Default", "Show", "Hide", }; +const char* kHorizontalAligments[] = { "Default", "Left", "Center", "Right", }; // Toggles bit |flag| on |flags| based on state of |checkbox|. void SetFlagFromCheckbox(Checkbox* checkbox, int* flags, int flag) { @@ -77,91 +60,67 @@ void SetFlagFromCheckbox(Checkbox* checkbox, int* flags, int flag) { } // namespace -// TextExample's content view, which is responsible for drawing a string with -// the specified style. +// TextExample's content view, which draws stylized string. class TextExample::TextExampleView : public View { public: TextExampleView() - : text_(ASCIIToUTF16(kShortText)), - text_flags_(0), + : text_(base::ASCIIToUTF16(kShortText)), + flags_(0), halo_(false), - fade_(false), - fade_mode_(gfx::Canvas::TruncateFadeTail) { + elide_(gfx::TRUNCATE) { } virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { View::OnPaint(canvas); const gfx::Rect bounds = GetContentsBounds(); - - if (fade_) { - canvas->DrawFadeTruncatingStringRect(text_, fade_mode_, font_list_, - SK_ColorDKGRAY, bounds); + const SkColor color = SK_ColorDKGRAY; + if (elide_ == gfx::FADE_TAIL) { + canvas->DrawFadedString(text_, font_list_, color, bounds, flags_); } else if (halo_) { - canvas->DrawStringRectWithHalo(text_, font_list_, SK_ColorDKGRAY, - SK_ColorWHITE, bounds, text_flags_); + canvas->DrawStringRectWithHalo(text_, font_list_, color, SK_ColorYELLOW, + bounds, flags_); } else { - canvas->DrawStringRectWithFlags(text_, font_list_, SK_ColorDKGRAY, bounds, - text_flags_); + canvas->DrawStringRectWithFlags(text_, font_list_, color, bounds, flags_); } } - int text_flags() const { return text_flags_; } - void set_text_flags(int text_flags) { text_flags_ = text_flags; } - - const string16& text() const { return text_; } - void set_text(const string16& text) { text_ = text; } - - bool halo() const { return halo_; } + int flags() const { return flags_; } + void set_flags(int flags) { flags_ = flags; } + void set_text(const base::string16& text) { text_ = text; } void set_halo(bool halo) { halo_ = halo; } + void set_elide(gfx::ElideBehavior elide) { elide_ = elide; } - bool fade() const { return fade_; } - void set_fade(bool fade) { fade_ = fade; } - - gfx::Canvas::TruncateFadeMode fade_mode() const { return fade_mode_; } - void set_fade_mode(gfx::Canvas::TruncateFadeMode mode) { fade_mode_ = mode; } - - int GetFontStyle() const { - return font_list_.GetFontStyle(); - } - void SetFontStyle(int style) { - font_list_ = font_list_.DeriveFontList(style); - } + int GetStyle() const { return font_list_.GetFontStyle(); } + void SetStyle(int style) { font_list_ = font_list_.DeriveWithStyle(style); } private: // The font used for drawing the text. gfx::FontList font_list_; // The text to draw. - string16 text_; + base::string16 text_; - // Text flags for passing to |DrawStringInt()|. - int text_flags_; + // Text flags for passing to |DrawStringRect()|. + int flags_; - // If |true|, specifies to call |DrawStringWithHalo()| instead of - // |DrawStringInt()|. + // A flag to draw a halo around the text. bool halo_; - // If |true|, specifies to call |DrawFadeTruncatingString()| instead of - // |DrawStringInt()|. - bool fade_; - - // If |fade_| is |true|, fade mode parameter to |DrawFadeTruncatingString()|. - gfx::Canvas::TruncateFadeMode fade_mode_; + // The eliding, fading, or truncating behavior. + gfx::ElideBehavior elide_; DISALLOW_COPY_AND_ASSIGN(TextExampleView); }; -TextExample::TextExample() : ExampleBase("Text Styles") { -} +TextExample::TextExample() : ExampleBase("Text Styles") {} TextExample::~TextExample() { - // Remove all the views first as some reference models in - // |example_combobox_model_|. + // Remove the views first as some reference combobox models. container()->RemoveAllChildViews(true); } Checkbox* TextExample::AddCheckbox(GridLayout* layout, const char* name) { - Checkbox* checkbox = new Checkbox(ASCIIToUTF16(name)); + Checkbox* checkbox = new Checkbox(base::ASCIIToUTF16(name)); checkbox->set_listener(this); layout->AddView(checkbox); return checkbox; @@ -172,11 +131,10 @@ Combobox* TextExample::AddCombobox(GridLayout* layout, const char** strings, int count) { layout->StartRow(0, 0); - layout->AddView(new Label(ASCIIToUTF16(name))); - ExampleComboboxModel* combobox_model = new ExampleComboboxModel(strings, - count); - example_combobox_model_.push_back(combobox_model); - Combobox* combobox = new Combobox(combobox_model); + layout->AddView(new Label(base::ASCIIToUTF16(name))); + ExampleComboboxModel* model = new ExampleComboboxModel(strings, count); + example_combobox_model_.push_back(model); + Combobox* combobox = new Combobox(model); combobox->SetSelectedIndex(0); combobox->set_listener(this); layout->AddView(combobox, kNumColumns - 1, 1); @@ -185,11 +143,9 @@ Combobox* TextExample::AddCombobox(GridLayout* layout, void TextExample::CreateExampleView(View* container) { text_view_ = new TextExampleView; - text_view_->set_border(Border::CreateSolidBorder(1, SK_ColorGRAY)); - + text_view_->SetBorder(Border::CreateSolidBorder(1, SK_ColorGRAY)); GridLayout* layout = new GridLayout(container); container->SetLayoutManager(layout); - layout->AddPaddingRow(0, 8); ColumnSet* column_set = layout->AddColumnSet(0); @@ -201,33 +157,24 @@ void TextExample::CreateExampleView(View* container) { 0.1f, GridLayout::USE_PREF, 0, 0); column_set->AddPaddingColumn(0, 8); - h_align_cb_ = AddCombobox(layout, - "H-Align", - kHorizontalAligments, + h_align_cb_ = AddCombobox(layout, "H-Align", kHorizontalAligments, arraysize(kHorizontalAligments)); - eliding_cb_ = AddCombobox(layout, - "Eliding", - kElidingBehaviors, - arraysize(kElidingBehaviors)); - prefix_cb_ = AddCombobox(layout, - "Prefix", - kPrefixOptions, + eliding_cb_ = AddCombobox(layout, "Eliding", kElideBehaviors, + arraysize(kElideBehaviors)); + prefix_cb_ = AddCombobox(layout, "Prefix", kPrefixOptions, arraysize(kPrefixOptions)); - text_cb_ = AddCombobox(layout, - "Example Text", - kTextExamples, + text_cb_ = AddCombobox(layout, "Example Text", kTextExamples, arraysize(kTextExamples)); layout->StartRow(0, 0); multiline_checkbox_ = AddCheckbox(layout, "Multiline"); break_checkbox_ = AddCheckbox(layout, "Character Break"); - halo_checkbox_ = AddCheckbox(layout, "Text Halo"); + halo_checkbox_ = AddCheckbox(layout, "Halo"); bold_checkbox_ = AddCheckbox(layout, "Bold"); italic_checkbox_ = AddCheckbox(layout, "Italic"); underline_checkbox_ = AddCheckbox(layout, "Underline"); - layout->AddPaddingRow(0, 32); - + layout->AddPaddingRow(0, 20); column_set = layout->AddColumnSet(1); column_set->AddPaddingColumn(0, 16); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, @@ -235,94 +182,83 @@ void TextExample::CreateExampleView(View* container) { column_set->AddPaddingColumn(0, 16); layout->StartRow(1, 1); layout->AddView(text_view_); - layout->AddPaddingRow(0, 8); } void TextExample::ButtonPressed(Button* button, const ui::Event& event) { - int flags = text_view_->text_flags(); - int style = text_view_->GetFontStyle(); + int flags = text_view_->flags(); + int style = text_view_->GetStyle(); SetFlagFromCheckbox(multiline_checkbox_, &flags, gfx::Canvas::MULTI_LINE); SetFlagFromCheckbox(break_checkbox_, &flags, gfx::Canvas::CHARACTER_BREAK); SetFlagFromCheckbox(bold_checkbox_, &style, gfx::Font::BOLD); SetFlagFromCheckbox(italic_checkbox_, &style, gfx::Font::ITALIC); SetFlagFromCheckbox(underline_checkbox_, &style, gfx::Font::UNDERLINE); text_view_->set_halo(halo_checkbox_->checked()); - text_view_->set_text_flags(flags); - text_view_->SetFontStyle(style); + text_view_->set_flags(flags); + text_view_->SetStyle(style); text_view_->SchedulePaint(); } -void TextExample::OnSelectedIndexChanged(Combobox* combobox) { - int text_flags = text_view_->text_flags(); +void TextExample::OnPerformAction(Combobox* combobox) { + int flags = text_view_->flags(); if (combobox == h_align_cb_) { - text_flags &= ~(gfx::Canvas::TEXT_ALIGN_LEFT | - gfx::Canvas::TEXT_ALIGN_CENTER | - gfx::Canvas::TEXT_ALIGN_RIGHT); + flags &= ~(gfx::Canvas::TEXT_ALIGN_LEFT | + gfx::Canvas::TEXT_ALIGN_CENTER | + gfx::Canvas::TEXT_ALIGN_RIGHT); switch (combobox->selected_index()) { case 0: break; case 1: - text_flags |= gfx::Canvas::TEXT_ALIGN_LEFT; + flags |= gfx::Canvas::TEXT_ALIGN_LEFT; break; case 2: - text_flags |= gfx::Canvas::TEXT_ALIGN_CENTER; + flags |= gfx::Canvas::TEXT_ALIGN_CENTER; break; case 3: - text_flags |= gfx::Canvas::TEXT_ALIGN_RIGHT; + flags |= gfx::Canvas::TEXT_ALIGN_RIGHT; break; } } else if (combobox == text_cb_) { switch (combobox->selected_index()) { case 0: - text_view_->set_text(ASCIIToUTF16(kShortText)); + text_view_->set_text(base::ASCIIToUTF16(kShortText)); break; case 1: - text_view_->set_text(ASCIIToUTF16(kMediumText)); + text_view_->set_text(base::ASCIIToUTF16(kLongText)); break; case 2: - text_view_->set_text(ASCIIToUTF16(kLongText)); + text_view_->set_text(base::ASCIIToUTF16(kAmpersandText)); break; case 3: - text_view_->set_text(ASCIIToUTF16(kAmpersandText)); - break; - case 4: - text_view_->set_text(ASCIIToUTF16(kNewlineText)); + text_view_->set_text(base::WideToUTF16(kRightToLeftText)); break; } } else if (combobox == eliding_cb_) { switch (combobox->selected_index()) { case 0: - text_flags &= ~gfx::Canvas::NO_ELLIPSIS; - text_view_->set_fade(false); + text_view_->set_elide(gfx::ELIDE_TAIL); break; case 1: - text_flags |= gfx::Canvas::NO_ELLIPSIS; - text_view_->set_fade(false); + text_view_->set_elide(gfx::TRUNCATE); break; case 2: - text_view_->set_fade_mode(gfx::Canvas::TruncateFadeTail); - text_view_->set_fade(true); - break; - case 3: - text_view_->set_fade_mode(gfx::Canvas::TruncateFadeHead); - text_view_->set_fade(true); + text_view_->set_elide(gfx::FADE_TAIL); break; } } else if (combobox == prefix_cb_) { - text_flags &= ~(gfx::Canvas::SHOW_PREFIX | gfx::Canvas::HIDE_PREFIX); + flags &= ~(gfx::Canvas::SHOW_PREFIX | gfx::Canvas::HIDE_PREFIX); switch (combobox->selected_index()) { case 0: break; case 1: - text_flags |= gfx::Canvas::SHOW_PREFIX; + flags |= gfx::Canvas::SHOW_PREFIX; break; case 2: - text_flags |= gfx::Canvas::HIDE_PREFIX; + flags |= gfx::Canvas::HIDE_PREFIX; break; } } - text_view_->set_text_flags(text_flags); + text_view_->set_flags(flags); text_view_->SchedulePaint(); } diff --git a/chromium/ui/views/examples/text_example.h b/chromium/ui/views/examples/text_example.h index 007f5194522..18ebaabdd74 100644 --- a/chromium/ui/views/examples/text_example.h +++ b/chromium/ui/views/examples/text_example.h @@ -5,8 +5,7 @@ #ifndef UI_VIEWS_EXAMPLES_TEXT_EXAMPLE_H_ #define UI_VIEWS_EXAMPLES_TEXT_EXAMPLE_H_ -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "base/macros.h" #include "base/memory/scoped_vector.h" #include "ui/views/controls/button/button.h" #include "ui/views/controls/combobox/combobox_listener.h" @@ -20,14 +19,14 @@ namespace examples { class ExampleComboboxModel; -class TextExample : public ExampleBase, - public ButtonListener, - public ComboboxListener { +class VIEWS_EXAMPLES_EXPORT TextExample : public ExampleBase, + public ButtonListener, + public ComboboxListener { public: TextExample(); virtual ~TextExample(); - // Overridden from ExampleBase: + // ExampleBase: virtual void CreateExampleView(View* container) OVERRIDE; private: @@ -40,11 +39,11 @@ class TextExample : public ExampleBase, const char** strings, int count); - // Overridden from ButtonListener: + // ButtonListener: virtual void ButtonPressed(Button* button, const ui::Event& event) OVERRIDE; - // Overridden from ComboboxListener: - virtual void OnSelectedIndexChanged(Combobox* combobox) OVERRIDE; + // ComboboxListener: + virtual void OnPerformAction(Combobox* combobox) OVERRIDE; class TextExampleView; // The content of the scroll view. diff --git a/chromium/ui/views/examples/textfield_example.cc b/chromium/ui/views/examples/textfield_example.cc index ec18cf74a5b..7dae434f353 100644 --- a/chromium/ui/views/examples/textfield_example.cc +++ b/chromium/ui/views/examples/textfield_example.cc @@ -14,6 +14,9 @@ #include "ui/views/layout/grid_layout.h" #include "ui/views/view.h" +using base::ASCIIToUTF16; +using base::UTF16ToUTF8; + namespace views { namespace examples { @@ -34,7 +37,8 @@ TextfieldExample::~TextfieldExample() { void TextfieldExample::CreateExampleView(View* container) { name_ = new Textfield(); - password_ = new Textfield(Textfield::STYLE_OBSCURED); + password_ = new Textfield(); + password_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); password_->set_placeholder_text(ASCIIToUTF16("password")); read_only_ = new Textfield(); read_only_->SetReadOnly(true); @@ -44,8 +48,8 @@ void TextfieldExample::CreateExampleView(View* container) { append_ = new LabelButton(this, ASCIIToUTF16("Append")); set_ = new LabelButton(this, ASCIIToUTF16("Set")); set_style_ = new LabelButton(this, ASCIIToUTF16("Set Styles")); - name_->SetController(this); - password_->SetController(this); + name_->set_controller(this); + password_->set_controller(this); GridLayout* layout = new GridLayout(container); container->SetLayoutManager(layout); @@ -77,7 +81,7 @@ void TextfieldExample::CreateExampleView(View* container) { } void TextfieldExample::ContentsChanged(Textfield* sender, - const string16& new_contents) { + const base::string16& new_contents) { if (sender == name_) { PrintStatus("Name [%s]", UTF16ToUTF8(new_contents).c_str()); } else if (sender == password_) { @@ -102,7 +106,7 @@ void TextfieldExample::ButtonPressed(Button* sender, const ui::Event& event) { if (sender == show_password_) { PrintStatus("Password [%s]", UTF16ToUTF8(password_->text()).c_str()); } else if (sender == clear_all_) { - string16 empty; + base::string16 empty; name_->SetText(empty); password_->SetText(empty); read_only_->SetText(empty); diff --git a/chromium/ui/views/examples/textfield_example.h b/chromium/ui/views/examples/textfield_example.h index b363c6ede7b..db5e1998299 100644 --- a/chromium/ui/views/examples/textfield_example.h +++ b/chromium/ui/views/examples/textfield_example.h @@ -7,9 +7,7 @@ #include <string> -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/strings/string16.h" +#include "base/macros.h" #include "ui/views/controls/button/button.h" #include "ui/views/controls/textfield/textfield_controller.h" #include "ui/views/examples/example_base.h" @@ -21,9 +19,9 @@ class LabelButton; namespace examples { // TextfieldExample mimics login screen. -class TextfieldExample : public ExampleBase, - public TextfieldController, - public ButtonListener { +class VIEWS_EXAMPLES_EXPORT TextfieldExample : public ExampleBase, + public TextfieldController, + public ButtonListener { public: TextfieldExample(); virtual ~TextfieldExample(); @@ -34,7 +32,7 @@ class TextfieldExample : public ExampleBase, private: // TextfieldController: virtual void ContentsChanged(Textfield* sender, - const string16& new_contents) OVERRIDE; + const base::string16& new_contents) OVERRIDE; virtual bool HandleKeyEvent(Textfield* sender, const ui::KeyEvent& key_event) OVERRIDE; virtual bool HandleMouseEvent(Textfield* sender, diff --git a/chromium/ui/views/examples/throbber_example.cc b/chromium/ui/views/examples/throbber_example.cc index ccc3c794432..7bd18a246cb 100644 --- a/chromium/ui/views/examples/throbber_example.cc +++ b/chromium/ui/views/examples/throbber_example.cc @@ -25,7 +25,7 @@ class ThrobberView : public View { throbber_->Start(); } - virtual gfx::Size GetPreferredSize() OVERRIDE { + virtual gfx::Size GetPreferredSize() const OVERRIDE { return gfx::Size(width(), height()); } diff --git a/chromium/ui/views/examples/throbber_example.h b/chromium/ui/views/examples/throbber_example.h index 646ab691f79..3c4f9c0beb2 100644 --- a/chromium/ui/views/examples/throbber_example.h +++ b/chromium/ui/views/examples/throbber_example.h @@ -5,19 +5,18 @@ #ifndef UI_VIEWS_EXAMPLES_THROBBER_EXAMPLE_H_ #define UI_VIEWS_EXAMPLES_THROBBER_EXAMPLE_H_ -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "base/macros.h" #include "ui/views/examples/example_base.h" namespace views { namespace examples { -class ThrobberExample : public ExampleBase { +class VIEWS_EXAMPLES_EXPORT ThrobberExample : public ExampleBase { public: ThrobberExample(); virtual ~ThrobberExample(); - // Overridden from ExampleBase: + // ExampleBase: virtual void CreateExampleView(View* container) OVERRIDE; private: diff --git a/chromium/ui/views/examples/tree_view_example.cc b/chromium/ui/views/examples/tree_view_example.cc index 6dfc5585974..2f6cc4cedf3 100644 --- a/chromium/ui/views/examples/tree_view_example.cc +++ b/chromium/ui/views/examples/tree_view_example.cc @@ -11,6 +11,8 @@ #include "ui/views/controls/tree/tree_view.h" #include "ui/views/layout/grid_layout.h" +using base::ASCIIToUTF16; + namespace views { namespace examples { @@ -130,10 +132,14 @@ void TreeViewExample::ShowContextMenuForView(View* source, context_menu_model.AddItem(ID_REMOVE, ASCIIToUTF16("Remove")); context_menu_model.AddItem(ID_ADD, ASCIIToUTF16("Add")); context_menu_runner_.reset(new MenuRunner(&context_menu_model)); - if (context_menu_runner_->RunMenuAt(source->GetWidget(), NULL, - gfx::Rect(point, gfx::Size()), MenuItemView::TOPLEFT, source_type, 0) == - MenuRunner::MENU_DELETED) + if (context_menu_runner_->RunMenuAt(source->GetWidget(), + NULL, + gfx::Rect(point, gfx::Size()), + MENU_ANCHOR_TOPLEFT, + source_type, + 0) == MenuRunner::MENU_DELETED) { return; + } } bool TreeViewExample::IsCommandIdChecked(int command_id) const { diff --git a/chromium/ui/views/examples/tree_view_example.h b/chromium/ui/views/examples/tree_view_example.h index ffac4dd66fa..55af17f7d08 100644 --- a/chromium/ui/views/examples/tree_view_example.h +++ b/chromium/ui/views/examples/tree_view_example.h @@ -5,8 +5,7 @@ #ifndef UI_VIEWS_EXAMPLES_TREE_VIEW_EXAMPLE_H_ #define UI_VIEWS_EXAMPLES_TREE_VIEW_EXAMPLE_H_ -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "base/macros.h" #include "ui/base/models/simple_menu_model.h" #include "ui/base/models/tree_node_model.h" #include "ui/views/context_menu_controller.h" @@ -21,11 +20,12 @@ class TreeView; namespace examples { -class TreeViewExample : public ExampleBase, - public ButtonListener, - public TreeViewController, - public ContextMenuController, - public ui::SimpleMenuModel::Delegate { +class VIEWS_EXAMPLES_EXPORT TreeViewExample + : public ExampleBase, + public ButtonListener, + public TreeViewController, + public ContextMenuController, + public ui::SimpleMenuModel::Delegate { public: TreeViewExample(); virtual ~TreeViewExample(); diff --git a/chromium/ui/views/examples/webview_example.cc b/chromium/ui/views/examples/webview_example.cc index 5a2d89d3904..feae6b977f9 100644 --- a/chromium/ui/views/examples/webview_example.cc +++ b/chromium/ui/views/examples/webview_example.cc @@ -6,7 +6,6 @@ #include "content/public/browser/browser_context.h" #include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_view.h" #include "ui/views/controls/webview/webview.h" #include "ui/views/layout/fill_layout.h" @@ -28,7 +27,7 @@ void WebViewExample::CreateExampleView(View* container) { container->AddChildView(webview_); webview_->LoadInitialURL(GURL("http://www.google.com/")); - webview_->web_contents()->GetView()->Focus(); + webview_->web_contents()->Focus(); } } // namespace examples diff --git a/chromium/ui/views/examples/webview_example.h b/chromium/ui/views/examples/webview_example.h index bc4e45918ef..3e35fe94a77 100644 --- a/chromium/ui/views/examples/webview_example.h +++ b/chromium/ui/views/examples/webview_example.h @@ -5,8 +5,7 @@ #ifndef UI_VIEWS_EXAMPLES_WEBVIEW_EXAMPLE_H_ #define UI_VIEWS_EXAMPLES_WEBVIEW_EXAMPLE_H_ -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "base/macros.h" #include "ui/views/examples/example_base.h" namespace content { @@ -23,7 +22,7 @@ class WebViewExample : public ExampleBase { explicit WebViewExample(content::BrowserContext* browser_context); virtual ~WebViewExample(); - // Overridden from ExampleBase: + // ExampleBase: virtual void CreateExampleView(View* container) OVERRIDE; private: diff --git a/chromium/ui/views/examples/widget_example.cc b/chromium/ui/views/examples/widget_example.cc index 0d42a65ae6b..1109a5ad4c4 100644 --- a/chromium/ui/views/examples/widget_example.cc +++ b/chromium/ui/views/examples/widget_example.cc @@ -13,6 +13,8 @@ #include "ui/views/widget/widget.h" #include "ui/views/window/dialog_delegate.h" +using base::ASCIIToUTF16; + namespace views { namespace examples { @@ -22,7 +24,7 @@ class DialogExample : public DialogDelegateView { public: DialogExample(); virtual ~DialogExample(); - virtual string16 GetWindowTitle() const OVERRIDE; + virtual base::string16 GetWindowTitle() const OVERRIDE; virtual View* CreateExtraView() OVERRIDE; virtual View* CreateTitlebarExtraView() OVERRIDE; virtual View* CreateFootnoteView() OVERRIDE; @@ -36,13 +38,13 @@ DialogExample::DialogExample() { DialogExample::~DialogExample() {} -string16 DialogExample::GetWindowTitle() const { +base::string16 DialogExample::GetWindowTitle() const { return ASCIIToUTF16("Dialog Widget Example"); } View* DialogExample::CreateExtraView() { LabelButton* button = new LabelButton(NULL, ASCIIToUTF16("Extra button!")); - button->SetStyle(Button::STYLE_NATIVE_TEXTBUTTON); + button->SetStyle(Button::STYLE_BUTTON); return button; } diff --git a/chromium/ui/views/examples/widget_example.h b/chromium/ui/views/examples/widget_example.h index 7c3085e02e8..ff08d70790f 100644 --- a/chromium/ui/views/examples/widget_example.h +++ b/chromium/ui/views/examples/widget_example.h @@ -7,8 +7,7 @@ #include <string> -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "base/macros.h" #include "ui/views/controls/button/button.h" #include "ui/views/examples/example_base.h" #include "ui/views/widget/widget.h" @@ -17,12 +16,13 @@ namespace views { namespace examples { // WidgetExample demonstrates how to create a popup widget. -class WidgetExample : public ExampleBase, public ButtonListener { +class VIEWS_EXAMPLES_EXPORT WidgetExample : public ExampleBase, + public ButtonListener { public: WidgetExample(); virtual ~WidgetExample(); - // Overridden from ExampleBase: + // ExampleBase: virtual void CreateExampleView(View* container) OVERRIDE; private: @@ -40,7 +40,7 @@ class WidgetExample : public ExampleBase, public ButtonListener { // Construct a Widget for |sender|, initialize with |params|, and call Show(). void ShowWidget(View* sender, Widget::InitParams params); - // Overridden from ButtonListener: + // ButtonListener: virtual void ButtonPressed(Button* sender, const ui::Event& event) OVERRIDE; DISALLOW_COPY_AND_ASSIGN(WidgetExample); diff --git a/chromium/ui/views/focus/accelerator_handler.h b/chromium/ui/views/focus/accelerator_handler.h deleted file mode 100644 index 29f353e697c..00000000000 --- a/chromium/ui/views/focus/accelerator_handler.h +++ /dev/null @@ -1,46 +0,0 @@ -// 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_FOCUS_ACCELERATOR_HANDLER_H_ -#define UI_VIEWS_FOCUS_ACCELERATOR_HANDLER_H_ - -#include "build/build_config.h" - -#include <set> -#include <vector> - -#include "base/compiler_specific.h" -#include "base/message_loop/message_loop.h" -#include "ui/views/views_export.h" - -namespace views { - -#if defined(USE_AURA) && defined(USE_X11) -// Dispatch an XEvent to the RootView. Return true if the event was dispatched -// and handled, false otherwise. -bool VIEWS_EXPORT DispatchXEvent(XEvent* xevent); -#endif // USE_AURA && USE_X11 - -// This class delegates the key messages to the associated FocusManager class -// for the window that is receiving these messages for accelerator processing. -class VIEWS_EXPORT AcceleratorHandler : public base::MessageLoop::Dispatcher { - public: - AcceleratorHandler(); - - // Dispatcher method. This returns true if an accelerator was processed by the - // focus manager - virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE; - - private: -#if defined(OS_WIN) - // The keys currently pressed and consumed by the FocusManager. - std::set<WPARAM> pressed_keys_; -#endif - - DISALLOW_COPY_AND_ASSIGN(AcceleratorHandler); -}; - -} // namespace views - -#endif // UI_VIEWS_FOCUS_ACCELERATOR_HANDLER_H_ diff --git a/chromium/ui/views/focus/accelerator_handler_aura.cc b/chromium/ui/views/focus/accelerator_handler_aura.cc deleted file mode 100644 index 8daa0f394e9..00000000000 --- a/chromium/ui/views/focus/accelerator_handler_aura.cc +++ /dev/null @@ -1,26 +0,0 @@ -// 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/focus/accelerator_handler.h" - -namespace views { - -AcceleratorHandler::AcceleratorHandler() { -} - -bool AcceleratorHandler::Dispatch(const base::NativeEvent& event) { -#if defined(OS_WIN) - TranslateMessage(&event); - DispatchMessage(&event); -#endif // defined(OS_WIN) - return true; -} - -#if defined(USE_X11) -bool DispatchXEvent(XEvent* xev) { - return false; -} -#endif // defined(USE_X11) - -} // namespace views diff --git a/chromium/ui/views/focus/accelerator_handler_win.cc b/chromium/ui/views/focus/accelerator_handler_win.cc deleted file mode 100644 index ea82daed717..00000000000 --- a/chromium/ui/views/focus/accelerator_handler_win.cc +++ /dev/null @@ -1,55 +0,0 @@ -// 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/focus/accelerator_handler.h" - -#include "ui/events/event.h" -#include "ui/events/keycodes/keyboard_code_conversion_win.h" -#include "ui/events/keycodes/keyboard_codes.h" -#include "ui/views/focus/focus_manager.h" -#include "ui/views/widget/widget.h" - -namespace views { - -AcceleratorHandler::AcceleratorHandler() { -} - -bool AcceleratorHandler::Dispatch(const base::NativeEvent& msg) { - if (msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST) { - Widget* widget = Widget::GetTopLevelWidgetForNativeView(msg.hwnd); - FocusManager* focus_manager = widget ? widget->GetFocusManager() : NULL; - if (focus_manager) { - switch (msg.message) { - case WM_KEYDOWN: - case WM_SYSKEYDOWN: { - ui::KeyEvent event(msg, false); - if (!focus_manager->OnKeyEvent(event)) { - // Record that this key is pressed so we can remember not to - // translate and dispatch the associated WM_KEYUP. - pressed_keys_.insert(msg.wParam); - return true; - } - break; - } - case WM_KEYUP: - case WM_SYSKEYUP: { - std::set<WPARAM>::iterator iter = pressed_keys_.find(msg.wParam); - if (iter != pressed_keys_.end()) { - // Don't translate/dispatch the KEYUP since we have eaten the - // associated KEYDOWN. - pressed_keys_.erase(iter); - return true; - } - break; - } - } - } - } - - TranslateMessage(&msg); - DispatchMessage(&msg); - return true; -} - -} // namespace views diff --git a/chromium/ui/views/focus/focus_manager.cc b/chromium/ui/views/focus/focus_manager.cc index f52712e6234..4808b4422cb 100644 --- a/chromium/ui/views/focus/focus_manager.cc +++ b/chromium/ui/views/focus/focus_manager.cc @@ -11,6 +11,10 @@ #include "base/logging.h" #include "build/build_config.h" #include "ui/base/accelerators/accelerator.h" +#include "ui/base/ime/input_method.h" +#include "ui/base/ime/text_input_client.h" +#include "ui/base/ime/text_input_focus_manager.h" +#include "ui/base/ui_base_switches_util.h" #include "ui/events/event.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/views/focus/focus_manager_delegate.h" @@ -64,6 +68,7 @@ bool FocusManager::OnKeyEvent(const ui::KeyEvent& event) { modifiers |= ui::EF_ALT_DOWN; ui::Accelerator accelerator(event.key_code(), modifiers); accelerator.set_type(event.type()); + accelerator.set_is_repeat(event.IsRepeat()); if (event.type() == ui::ET_KEY_PRESSED) { // If the focused view wants to process the key event as is, let it be. @@ -75,20 +80,10 @@ bool FocusManager::OnKeyEvent(const ui::KeyEvent& event) { // Note that we don't do focus traversal if the root window is not part of // the active window hierarchy as this would mean we have no focused view // and would focus the first focusable view. -#if defined(OS_WIN) && !defined(USE_AURA) - HWND top_window = widget_->GetNativeView(); - HWND active_window = ::GetActiveWindow(); - if ((active_window == top_window || ::IsChild(active_window, top_window)) && - IsTabTraversalKeyEvent(event)) { - AdvanceFocus(event.IsShiftDown()); - return false; - } -#else if (IsTabTraversalKeyEvent(event)) { AdvanceFocus(event.IsShiftDown()); return false; } -#endif if (arrow_key_traversal_enabled_ && ProcessArrowKeyTraversal(event)) return false; @@ -319,8 +314,13 @@ View* FocusManager::GetNextFocusableView(View* original_starting_view, void FocusManager::SetFocusedViewWithReason( View* view, FocusChangeReason reason) { - if (focused_view_ == view) + if (focused_view_ == view) { + // In the case that the widget lost the focus and gained it back without + // changing the focused view, we have to make the text input client focused. + // TODO(yukishiino): Remove this hack once we fix http://crbug.com/383236 + FocusTextInputClient(focused_view_); return; + } base::AutoReset<bool> auto_changing_focus(&is_changing_focus_, true); // Update the reason for the focus change (since this is checked by @@ -331,14 +331,18 @@ void FocusManager::SetFocusedViewWithReason( View* old_focused_view = focused_view_; focused_view_ = view; - if (old_focused_view) + if (old_focused_view) { old_focused_view->Blur(); + BlurTextInputClient(old_focused_view); + } // Also make |focused_view_| the stored focus view. This way the stored focus // view is remembered if focus changes are requested prior to a show or while // hidden. SetStoredFocusView(focused_view_); - if (focused_view_) + if (focused_view_) { + FocusTextInputClient(focused_view_); focused_view_->Focus(); + } FOR_EACH_OBSERVER(FocusChangeListener, focus_change_listeners_, OnDidChangeFocus(old_focused_view, focused_view_)); @@ -437,6 +441,46 @@ void FocusManager::ClearStoredFocusedView() { SetStoredFocusView(NULL); } +void FocusManager::OnTextInputClientChanged(View* view) { + if (view == focused_view_) + FocusTextInputClient(view); +} + +void FocusManager::FocusTextInputClient(View* view) { + if (!switches::IsTextInputFocusManagerEnabled()) + return; + + // If the widget is not active, do not steal the text input focus. + if (!widget_->IsActive()) + return; + + ui::TextInputClient* text_input_client = + view ? view->GetTextInputClient() : NULL; + ui::TextInputFocusManager::GetInstance()-> + FocusTextInputClient(text_input_client); + ui::InputMethod* input_method = widget_->GetHostInputMethod(); + if (input_method) { + input_method->OnTextInputTypeChanged(text_input_client); + input_method->OnCaretBoundsChanged(text_input_client); + } +} + +void FocusManager::BlurTextInputClient(View* view) { + if (!switches::IsTextInputFocusManagerEnabled()) + return; + + ui::TextInputClient* text_input_client = + view ? view->GetTextInputClient() : NULL; + if (text_input_client && text_input_client->HasCompositionText()) { + text_input_client->ConfirmCompositionText(); + ui::InputMethod* input_method = widget_->GetHostInputMethod(); + if (input_method && input_method->GetTextInputClient() == text_input_client) + input_method->CancelComposition(text_input_client); + } + ui::TextInputFocusManager::GetInstance()-> + BlurTextInputClient(text_input_client); +} + // Find the next (previous if reverse is true) focusable view for the specified // FocusTraversable, starting at the specified view, traversing down the // FocusTraversable hierarchy. diff --git a/chromium/ui/views/focus/focus_manager.h b/chromium/ui/views/focus/focus_manager.h index 7d7c7dc16e0..ebdc2a3d88e 100644 --- a/chromium/ui/views/focus/focus_manager.h +++ b/chromium/ui/views/focus/focus_manager.h @@ -72,8 +72,8 @@ // is FocusTraversable. namespace ui { -class AcceleratorTarget; class AcceleratorManager; +class AcceleratorTarget; class EventHandler; class KeyEvent; } @@ -213,6 +213,15 @@ class VIEWS_EXPORT FocusManager { // Returns true if in the process of changing the focused view. bool is_changing_focus() const { return is_changing_focus_; } + // Changes the text input focus to |view->GetTextInputClient()| iff |view| + // is focused. Views must call this method when their internal + // TextInputClient instance changes. + void OnTextInputClientChanged(View* view); + + // Moves the text input focus into/out from |view|. + void FocusTextInputClient(View* view); + void BlurTextInputClient(View* view); + // Disable shortcut handling. static void set_shortcut_handling_suspended(bool suspended) { shortcut_handling_suspended_ = suspended; diff --git a/chromium/ui/views/focus/focus_manager_test.cc b/chromium/ui/views/focus/focus_manager_test.cc index 528f345ce63..6debc794dff 100644 --- a/chromium/ui/views/focus/focus_manager_test.cc +++ b/chromium/ui/views/focus/focus_manager_test.cc @@ -100,24 +100,6 @@ void FocusManagerTest::SetAccessiblePanes(const std::vector<View*>& panes) { accessible_panes_ = panes; } -#if defined(OS_WIN) && !defined(USE_AURA) -void FocusManagerTest::SimulateActivateWindow() { - SendMessage(GetWidget()->GetNativeWindow(), WM_ACTIVATE, WA_ACTIVE, NULL); -} - -void FocusManagerTest::SimulateDeactivateWindow() { - SendMessage(GetWidget()->GetNativeWindow(), WM_ACTIVATE, WA_INACTIVE, NULL); -} - -void FocusManagerTest::PostKeyDown(ui::KeyboardCode key_code) { - PostMessage(GetWidget()->GetNativeView(), WM_KEYDOWN, key_code, 0); -} - -void FocusManagerTest::PostKeyUp(ui::KeyboardCode key_code) { - PostMessage(GetWidget()->GetNativeView(), WM_KEYUP, key_code, 0); -} -#endif - //////////////////////////////////////////////////////////////////////////////// // TestFocusChangeListener diff --git a/chromium/ui/views/focus/focus_manager_test.h b/chromium/ui/views/focus/focus_manager_test.h index 4afbf4f7758..e54138673b0 100644 --- a/chromium/ui/views/focus/focus_manager_test.h +++ b/chromium/ui/views/focus/focus_manager_test.h @@ -43,15 +43,6 @@ class FocusManagerTest : public ViewsTestBase, public WidgetDelegate { // For testing FocusManager::RotatePaneFocus(). void SetAccessiblePanes(const std::vector<View*>& panes); -#if defined(OS_WIN) && !defined(USE_AURA) - // Mocks activating/deactivating the window. - void SimulateActivateWindow(); - void SimulateDeactivateWindow(); - - void PostKeyDown(ui::KeyboardCode key_code); - void PostKeyUp(ui::KeyboardCode key_code); -#endif - private: View* contents_view_; FocusChangeListener* focus_change_listener_; diff --git a/chromium/ui/views/focus/focus_manager_unittest.cc b/chromium/ui/views/focus/focus_manager_unittest.cc index fcb82b7f310..3513e2b70a7 100644 --- a/chromium/ui/views/focus/focus_manager_unittest.cc +++ b/chromium/ui/views/focus/focus_manager_unittest.cc @@ -2,38 +2,29 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "ui/views/focus/focus_manager.h" + #include <utility> #include <vector> +#include "base/command_line.h" #include "base/strings/utf_string_conversions.h" +#include "ui/aura/client/focus_client.h" +#include "ui/aura/window.h" #include "ui/base/accelerators/accelerator.h" +#include "ui/base/ime/dummy_text_input_client.h" +#include "ui/base/ime/text_input_focus_manager.h" +#include "ui/base/ui_base_switches.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/views/accessible_pane_view.h" #include "ui/views/controls/button/label_button.h" -#include "ui/views/controls/textfield/textfield.h" -#include "ui/views/focus/accelerator_handler.h" #include "ui/views/focus/focus_manager_factory.h" #include "ui/views/focus/focus_manager_test.h" #include "ui/views/focus/widget_focus_manager.h" #include "ui/views/widget/widget.h" -#if defined(USE_AURA) -#include "ui/aura/client/focus_client.h" -#include "ui/aura/window.h" -#endif - namespace views { -void FocusNativeView(gfx::NativeView view) { -#if defined(USE_AURA) - aura::client::GetFocusClient(view)->FocusWindow(view); -#elif defined(OS_WIN) - SetFocus(view); -#else -#error -#endif -} - enum FocusTestEventType { ON_FOCUS = 0, ON_BLUR @@ -67,6 +58,8 @@ class SimpleTestView : public View { private: std::vector<FocusTestEvent>* event_list_; + + DISALLOW_COPY_AND_ASSIGN(SimpleTestView); }; // Tests that the appropriate Focus related methods are called when a View @@ -149,14 +142,14 @@ TEST_F(FocusManagerTest, WidgetFocusChangeListener) { widget_listener.ClearFocusChanges(); gfx::NativeView native_view1 = widget1->GetNativeView(); - FocusNativeView(native_view1); + aura::client::GetFocusClient(native_view1)->FocusWindow(native_view1); ASSERT_EQ(2, static_cast<int>(widget_listener.focus_changes().size())); EXPECT_EQ(native_view1, widget_listener.focus_changes()[0].second); EXPECT_EQ(native_view1, widget_listener.focus_changes()[1].second); widget_listener.ClearFocusChanges(); gfx::NativeView native_view2 = widget2->GetNativeView(); - FocusNativeView(native_view2); + aura::client::GetFocusClient(native_view2)->FocusWindow(native_view2); ASSERT_EQ(2, static_cast<int>(widget_listener.focus_changes().size())); EXPECT_EQ(NativeViewPair(native_view1, native_view2), widget_listener.focus_changes()[0]); @@ -164,26 +157,6 @@ TEST_F(FocusManagerTest, WidgetFocusChangeListener) { widget_listener.focus_changes()[1]); } -#if !defined(USE_AURA) -class TestTextfield : public Textfield { - public: - TestTextfield() {} - virtual gfx::NativeView TestGetNativeControlView() { - return native_wrapper_->GetTestingHandle(); - } -}; - -// Tests that NativeControls do set the focused View appropriately on the -// FocusManager. -TEST_F(FocusManagerTest, DISABLED_FocusNativeControls) { - TestTextfield* textfield = new TestTextfield(); - GetContentsView()->AddChildView(textfield); - // Simulate the native view getting the native focus (such as by user click). - FocusNativeView(textfield->TestGetNativeControlView()); - EXPECT_EQ(textfield, GetFocusManager()->GetFocusedView()); -} -#endif - // Counts accelerator calls. class TestAcceleratorTarget : public ui::AcceleratorTarget { public: @@ -537,10 +510,11 @@ class FocusManagerDtorTest : public FocusManagerTest { class LabelButtonDtorTracked : public LabelButton { public: - LabelButtonDtorTracked(const string16& text, DtorTrackVector* dtor_tracker) + LabelButtonDtorTracked(const base::string16& text, + DtorTrackVector* dtor_tracker) : LabelButton(NULL, text), dtor_tracker_(dtor_tracker) { - SetStyle(STYLE_NATIVE_TEXTBUTTON); + SetStyle(STYLE_BUTTON); }; virtual ~LabelButtonDtorTracked() { dtor_tracker_->push_back("LabelButtonDtorTracked"); @@ -586,25 +560,6 @@ class FocusManagerDtorTest : public FocusManagerTest { DtorTrackVector dtor_tracker_; }; -#if !defined(USE_AURA) -TEST_F(FocusManagerDtorTest, FocusManagerDestructedLast) { - // Setup views hierarchy. - GetContentsView()->AddChildView(new TestTextfield()); - GetContentsView()->AddChildView(new LabelButtonDtorTracked( - ASCIIToUTF16("button"), &dtor_tracker_)); - - // Close the window. - GetWidget()->Close(); - RunPendingMessages(); - - // Test window, button and focus manager should all be destructed. - ASSERT_EQ(3, static_cast<int>(dtor_tracker_.size())); - - // Focus manager should be the last one to destruct. - ASSERT_STREQ("FocusManagerDtorTracked", dtor_tracker_[2].c_str()); -} -#endif - namespace { class FocusInAboutToRequestFocusFromTabTraversalView : public View { @@ -819,16 +774,72 @@ TEST_F(FocusManagerTest, StoreFocusedView) { View view; GetFocusManager()->SetFocusedView(&view); GetFocusManager()->StoreFocusedView(false); + EXPECT_EQ(NULL, GetFocusManager()->GetFocusedView()); EXPECT_TRUE(GetFocusManager()->RestoreFocusedView()); EXPECT_EQ(&view, GetFocusManager()->GetStoredFocusView()); // Repeat with |true|. GetFocusManager()->SetFocusedView(&view); GetFocusManager()->StoreFocusedView(true); + EXPECT_EQ(NULL, GetFocusManager()->GetFocusedView()); EXPECT_TRUE(GetFocusManager()->RestoreFocusedView()); EXPECT_EQ(&view, GetFocusManager()->GetStoredFocusView()); } +class TextInputTestView : public View { + public: + TextInputTestView() {} + + virtual ui::TextInputClient* GetTextInputClient() OVERRIDE { + return &text_input_client_; + } + + private: + ui::DummyTextInputClient text_input_client_; + + DISALLOW_COPY_AND_ASSIGN(TextInputTestView); +}; + +TEST_F(FocusManagerTest, TextInputClient) { + base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); + cmd_line->AppendSwitch(switches::kEnableTextInputFocusManager); + + View* view = new TextInputTestView; + ui::TextInputClient* text_input_client = view->GetTextInputClient(); + view->SetFocusable(true); + GetContentsView()->AddChildView(view); + ui::TextInputFocusManager* text_input_focus_manager = + ui::TextInputFocusManager::GetInstance(); + + GetFocusManager()->SetFocusedView(view); + EXPECT_EQ(view, GetFocusManager()->GetFocusedView()); + EXPECT_EQ(text_input_client, + text_input_focus_manager->GetFocusedTextInputClient()); + GetFocusManager()->StoreFocusedView(false); + EXPECT_TRUE(GetFocusManager()->RestoreFocusedView()); + EXPECT_EQ(text_input_client, + text_input_focus_manager->GetFocusedTextInputClient()); + + // Repeat with |true|. + GetFocusManager()->SetFocusedView(view); + EXPECT_EQ(view, GetFocusManager()->GetFocusedView()); + EXPECT_EQ(text_input_client, + text_input_focus_manager->GetFocusedTextInputClient()); + GetFocusManager()->StoreFocusedView(true); + EXPECT_TRUE(GetFocusManager()->RestoreFocusedView()); + EXPECT_EQ(text_input_client, + text_input_focus_manager->GetFocusedTextInputClient()); + + // Focus the view twice in a row. + GetFocusManager()->SetFocusedView(view); + EXPECT_EQ(text_input_client, + text_input_focus_manager->GetFocusedTextInputClient()); + ui::TextInputFocusManager::GetInstance()->FocusTextInputClient(NULL); + GetFocusManager()->SetFocusedView(view); + EXPECT_EQ(text_input_client, + text_input_focus_manager->GetFocusedTextInputClient()); +} + namespace { // Trivial WidgetDelegate implementation that allows setting return value of diff --git a/chromium/ui/views/focus/focus_manager_unittest_win.cc b/chromium/ui/views/focus/focus_manager_unittest_win.cc deleted file mode 100644 index 267a8b78df3..00000000000 --- a/chromium/ui/views/focus/focus_manager_unittest_win.cc +++ /dev/null @@ -1,281 +0,0 @@ -// 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/focus/focus_manager.h" - -#include "base/memory/scoped_ptr.h" -#include "base/run_loop.h" -#include "base/strings/utf_string_conversions.h" -#include "ui/events/event.h" -#include "ui/views/controls/button/label_button.h" -#include "ui/views/focus/accelerator_handler.h" -#include "ui/views/focus/focus_manager_test.h" -#include "ui/views/widget/widget.h" - -namespace views { - -namespace { - -class MessageTrackingView : public View { - public: - MessageTrackingView() : accelerator_pressed_(false) { - } - - void Reset() { - accelerator_pressed_ = false; - keys_pressed_.clear(); - keys_released_.clear(); - } - - const std::vector<ui::KeyboardCode>& keys_pressed() const { - return keys_pressed_; - } - - const std::vector<ui::KeyboardCode>& keys_released() const { - return keys_released_; - } - - bool accelerator_pressed() const { - return accelerator_pressed_; - } - - // Overridden from View: - virtual bool OnKeyPressed(const ui::KeyEvent& e) OVERRIDE { - keys_pressed_.push_back(e.key_code()); - return true; - } - virtual bool OnKeyReleased(const ui::KeyEvent& e) OVERRIDE { - keys_released_.push_back(e.key_code()); - return true; - } - virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE { - accelerator_pressed_ = true; - return true; - } - - private: - bool accelerator_pressed_; - std::vector<ui::KeyboardCode> keys_pressed_; - std::vector<ui::KeyboardCode> keys_released_; - - DISALLOW_COPY_AND_ASSIGN(MessageTrackingView); -}; - -} // namespace - -// Test that when activating/deactivating the top window, the focus is stored/ -// restored properly. -TEST_F(FocusManagerTest, FocusStoreRestore) { - // Simulate an activate, otherwise the deactivate isn't going to do anything. - SimulateActivateWindow(); - - LabelButton* button = new LabelButton(NULL, ASCIIToUTF16("Press me")); - button->SetStyle(Button::STYLE_NATIVE_TEXTBUTTON); - View* view = new View(); - view->SetFocusable(true); - - GetContentsView()->AddChildView(button); - button->SetBounds(10, 10, 200, 30); - GetContentsView()->AddChildView(view); - RunPendingMessages(); - - TestFocusChangeListener listener; - AddFocusChangeListener(&listener); - - view->RequestFocus(); - RunPendingMessages(); - - // Required for VS2010: http://connect.microsoft.com/VisualStudio/feedback/details/520043/error-converting-from-null-to-a-pointer-type-in-std-pair - views::View* null_view = NULL; - - // Deacivate the window, it should store its focus. - SimulateDeactivateWindow(); - EXPECT_EQ(NULL, GetFocusManager()->GetFocusedView()); - ASSERT_EQ(2, static_cast<int>(listener.focus_changes().size())); - EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(null_view, view)); - EXPECT_TRUE(listener.focus_changes()[1] == ViewPair(view, null_view)); - listener.ClearFocusChanges(); - - // Reactivate, focus should come-back to the previously focused view. - SimulateActivateWindow(); - EXPECT_EQ(view, GetFocusManager()->GetFocusedView()); - ASSERT_EQ(1, static_cast<int>(listener.focus_changes().size())); - EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(null_view, view)); - listener.ClearFocusChanges(); - - // Same test with a NativeControl. - button->RequestFocus(); - SimulateDeactivateWindow(); - EXPECT_EQ(NULL, GetFocusManager()->GetFocusedView()); - ASSERT_EQ(2, static_cast<int>(listener.focus_changes().size())); - EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(view, button)); - EXPECT_TRUE(listener.focus_changes()[1] == ViewPair(button, null_view)); - listener.ClearFocusChanges(); - - SimulateActivateWindow(); - EXPECT_EQ(button, GetFocusManager()->GetFocusedView()); - ASSERT_EQ(1, static_cast<int>(listener.focus_changes().size())); - EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(null_view, button)); - listener.ClearFocusChanges(); - - /* - // Now test that while the window is inactive we can change the focused view - // (we do that in several places). - SimulateDeactivateWindow(); - // TODO: would have to mock the window being inactive (with a TestWidgetWin - // that would return false on IsActive()). - GetFocusManager()->SetFocusedView(view); - ::SendMessage(window_->GetNativeWindow(), WM_ACTIVATE, WA_ACTIVE, NULL); - - EXPECT_EQ(view, GetFocusManager()->GetFocusedView()); - ASSERT_EQ(2, static_cast<int>(listener.focus_changes().size())); - EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(button, null_view)); - EXPECT_TRUE(listener.focus_changes()[1] == ViewPair(null_view, view)); - */ -} - -// Test that the focus manager is created successfully for the first view -// window parented to a native dialog. -TEST_F(FocusManagerTest, CreationForNativeRoot) { - // Create a window class. - WNDCLASSEX class_ex; - memset(&class_ex, 0, sizeof(class_ex)); - class_ex.cbSize = sizeof(WNDCLASSEX); - class_ex.lpfnWndProc = &DefWindowProc; - class_ex.lpszClassName = L"TestWindow"; - ATOM atom = RegisterClassEx(&class_ex); - ASSERT_TRUE(atom); - - // Create a native dialog window. - HWND hwnd = CreateWindowEx(0, class_ex.lpszClassName, NULL, - WS_OVERLAPPEDWINDOW, 0, 0, 200, 200, - NULL, NULL, NULL, NULL); - ASSERT_TRUE(hwnd); - - // Create a view window parented to native dialog. - scoped_ptr<Widget> widget1(new Widget); - Widget::InitParams params(Widget::InitParams::TYPE_CONTROL); - params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - params.parent = hwnd; - params.bounds = gfx::Rect(0, 0, 100, 100); - params.top_level = true; // This is top level in views hierarchy. - widget1->Init(params); - - // Get the focus manager directly from the first window. Should exist - // because the first window is the root widget. - views::FocusManager* focus_manager1 = widget1->GetFocusManager(); - EXPECT_TRUE(focus_manager1); - - // Create another view window parented to the first view window. - scoped_ptr<Widget> widget2(new Widget); - params.parent = widget1->GetNativeView(); - params.top_level = false; // This is child widget. - widget2->Init(params); - - // Access the shared focus manager directly from the second window. - views::FocusManager* focus_manager2 = widget2->GetFocusManager(); - EXPECT_EQ(focus_manager2, focus_manager1); - - // Access the shared focus manager indirectly from the first window handle. - gfx::NativeWindow native_window = widget1->GetNativeWindow(); - views::Widget* widget = - views::Widget::GetWidgetForNativeWindow(native_window); - EXPECT_EQ(widget->GetFocusManager(), focus_manager1); - - // Access the shared focus manager indirectly from the second window handle. - native_window = widget2->GetNativeWindow(); - widget = views::Widget::GetWidgetForNativeWindow(native_window); - EXPECT_EQ(widget->GetFocusManager(), focus_manager1); - - // Access the shared focus manager indirectly from the first view handle. - gfx::NativeView native_view = widget1->GetNativeView(); - widget = views::Widget::GetTopLevelWidgetForNativeView(native_view); - EXPECT_EQ(widget->GetFocusManager(), focus_manager1); - - // Access the shared focus manager indirectly from the second view handle. - native_view = widget2->GetNativeView(); - widget = views::Widget::GetTopLevelWidgetForNativeView(native_view); - EXPECT_EQ(widget->GetFocusManager(), focus_manager1); - - DestroyWindow(hwnd); -} - -// Tests that the keyup messages are eaten for accelerators. -// Windows-only, Windows is the only platform that handles accelerators in -// AcceleratorHandler. NativeWidgetAura::OnKeyEvent handles them in other -// configurations. -TEST_F(FocusManagerTest, IgnoreKeyupForAccelerators) { - FocusManager* focus_manager = GetFocusManager(); - MessageTrackingView* mtv = new MessageTrackingView(); - mtv->AddAccelerator(ui::Accelerator(ui::VKEY_0, ui::EF_NONE)); - mtv->AddAccelerator(ui::Accelerator(ui::VKEY_1, ui::EF_NONE)); - GetContentsView()->AddChildView(mtv); - focus_manager->SetFocusedView(mtv); - - // First send a non-accelerator key sequence. - PostKeyDown(ui::VKEY_9); - PostKeyUp(ui::VKEY_9); - AcceleratorHandler accelerator_handler; - scoped_ptr<base::RunLoop> run_loop(new base::RunLoop(&accelerator_handler)); - run_loop->RunUntilIdle(); - // Make sure we get a key-up and key-down. - ASSERT_EQ(1U, mtv->keys_pressed().size()); - EXPECT_EQ(ui::VKEY_9, mtv->keys_pressed()[0]); - ASSERT_EQ(1U, mtv->keys_released().size()); - EXPECT_EQ(ui::VKEY_9, mtv->keys_released()[0]); - EXPECT_FALSE(mtv->accelerator_pressed()); - mtv->Reset(); - - // Same thing with repeat and more than one key at once. - PostKeyDown(ui::VKEY_9); - PostKeyDown(ui::VKEY_9); - PostKeyDown(ui::VKEY_8); - PostKeyDown(ui::VKEY_9); - PostKeyDown(ui::VKEY_7); - PostKeyUp(ui::VKEY_9); - PostKeyUp(ui::VKEY_7); - PostKeyUp(ui::VKEY_8); - run_loop.reset(new base::RunLoop(&accelerator_handler)); - run_loop->RunUntilIdle(); - // Make sure we get a key-up and key-down. - ASSERT_EQ(5U, mtv->keys_pressed().size()); - EXPECT_EQ(ui::VKEY_9, mtv->keys_pressed()[0]); - EXPECT_EQ(ui::VKEY_9, mtv->keys_pressed()[1]); - EXPECT_EQ(ui::VKEY_8, mtv->keys_pressed()[2]); - EXPECT_EQ(ui::VKEY_9, mtv->keys_pressed()[3]); - EXPECT_EQ(ui::VKEY_7, mtv->keys_pressed()[4]); - ASSERT_EQ(3U, mtv->keys_released().size()); - EXPECT_EQ(ui::VKEY_9, mtv->keys_released()[0]); - EXPECT_EQ(ui::VKEY_7, mtv->keys_released()[1]); - EXPECT_EQ(ui::VKEY_8, mtv->keys_released()[2]); - EXPECT_FALSE(mtv->accelerator_pressed()); - mtv->Reset(); - - // Now send an accelerator key sequence. - PostKeyDown(ui::VKEY_0); - PostKeyUp(ui::VKEY_0); - run_loop.reset(new base::RunLoop(&accelerator_handler)); - run_loop->RunUntilIdle(); - EXPECT_TRUE(mtv->keys_pressed().empty()); - EXPECT_TRUE(mtv->keys_released().empty()); - EXPECT_TRUE(mtv->accelerator_pressed()); - mtv->Reset(); - - // Same thing with repeat and more than one key at once. - PostKeyDown(ui::VKEY_0); - PostKeyDown(ui::VKEY_1); - PostKeyDown(ui::VKEY_1); - PostKeyDown(ui::VKEY_0); - PostKeyDown(ui::VKEY_0); - PostKeyUp(ui::VKEY_1); - PostKeyUp(ui::VKEY_0); - run_loop.reset(new base::RunLoop(&accelerator_handler)); - run_loop->RunUntilIdle(); - EXPECT_TRUE(mtv->keys_pressed().empty()); - EXPECT_TRUE(mtv->keys_released().empty()); - EXPECT_TRUE(mtv->accelerator_pressed()); - mtv->Reset(); -} - -} // namespace views diff --git a/chromium/ui/views/focus/focus_traversal_unittest.cc b/chromium/ui/views/focus/focus_traversal_unittest.cc index c3e8159a5fa..6f0ba34ecb2 100644 --- a/chromium/ui/views/focus/focus_traversal_unittest.cc +++ b/chromium/ui/views/focus/focus_traversal_unittest.cc @@ -20,11 +20,12 @@ #include "ui/views/controls/scroll_view.h" #include "ui/views/controls/tabbed_pane/tabbed_pane.h" #include "ui/views/controls/textfield/textfield.h" -#include "ui/views/focus/accelerator_handler.h" #include "ui/views/focus/focus_manager_test.h" #include "ui/views/widget/root_view.h" #include "ui/views/widget/widget.h" +using base::ASCIIToUTF16; + namespace views { namespace { @@ -89,7 +90,7 @@ class DummyComboboxModel : public ui::ComboboxModel { public: // Overridden from ui::ComboboxModel: virtual int GetItemCount() const OVERRIDE { return 10; } - virtual string16 GetItemAt(int index) OVERRIDE { + virtual base::string16 GetItemAt(int index) OVERRIDE { return ASCIIToUTF16("Item ") + base::IntToString16(index); } }; @@ -157,11 +158,7 @@ class BorderView : public NativeViewHost { if (!widget_) { widget_ = new Widget; Widget::InitParams params(Widget::InitParams::TYPE_CONTROL); -#if defined(OS_WIN) || defined(USE_AURA) params.parent = details.parent->GetWidget()->GetNativeView(); -#else - NOTREACHED(); -#endif widget_->Init(params); widget_->SetFocusTraversableParentView(this); widget_->SetContentsView(child_); @@ -294,7 +291,7 @@ void FocusTraversalTest::InitContentView() { cb->set_id(kTopCheckBoxID); left_container_ = new PaneView(); - left_container_->set_border(Border::CreateSolidBorder(1, SK_ColorBLACK)); + left_container_->SetBorder(Border::CreateSolidBorder(1, SK_ColorBLACK)); left_container_->set_background( Background::CreateSolidBackground(240, 240, 240)); left_container_->set_id(kLeftContainerID); @@ -361,7 +358,7 @@ void FocusTraversalTest::InitContentView() { y += label_height + gap_between_labels; LabelButton* button = new LabelButton(NULL, ASCIIToUTF16("Click me")); - button->SetStyle(Button::STYLE_NATIVE_TEXTBUTTON); + button->SetStyle(Button::STYLE_BUTTON); button->SetBounds(label_x, y + 10, 80, 30); button->set_id(kFruitButtonID); left_container_->AddChildView(button); @@ -379,7 +376,7 @@ void FocusTraversalTest::InitContentView() { left_container_->AddChildView(combobox); right_container_ = new PaneView(); - right_container_->set_border(Border::CreateSolidBorder(1, SK_ColorBLACK)); + right_container_->SetBorder(Border::CreateSolidBorder(1, SK_ColorBLACK)); right_container_->set_background( Background::CreateSolidBackground(240, 240, 240)); right_container_->set_id(kRightContainerID); @@ -410,7 +407,7 @@ void FocusTraversalTest::InitContentView() { y += radio_button_height + gap_between_radio_buttons; View* inner_container = new View(); - inner_container->set_border(Border::CreateSolidBorder(1, SK_ColorBLACK)); + inner_container->SetBorder(Border::CreateSolidBorder(1, SK_ColorBLACK)); inner_container->set_background( Background::CreateSolidBackground(230, 230, 230)); inner_container->set_id(kInnerContainerID); @@ -457,7 +454,7 @@ void FocusTraversalTest::InitContentView() { y = 250; int width = 60; button = new LabelButton(NULL, ASCIIToUTF16("OK")); - button->SetStyle(Button::STYLE_NATIVE_TEXTBUTTON); + button->SetStyle(Button::STYLE_BUTTON); button->set_id(kOKButtonID); button->SetIsDefault(true); @@ -465,13 +462,13 @@ void FocusTraversalTest::InitContentView() { button->SetBounds(150, y, width, 30); button = new LabelButton(NULL, ASCIIToUTF16("Cancel")); - button->SetStyle(Button::STYLE_NATIVE_TEXTBUTTON); + button->SetStyle(Button::STYLE_BUTTON); button->set_id(kCancelButtonID); GetContentsView()->AddChildView(button); button->SetBounds(220, y, width, 30); button = new LabelButton(NULL, ASCIIToUTF16("Help")); - button->SetStyle(Button::STYLE_NATIVE_TEXTBUTTON); + button->SetStyle(Button::STYLE_BUTTON); button->set_id(kHelpButtonID); GetContentsView()->AddChildView(button); button->SetBounds(290, y, width, 30); @@ -525,7 +522,7 @@ void FocusTraversalTest::InitContentView() { text_field->set_id(kSearchTextfieldID); button = new LabelButton(NULL, ASCIIToUTF16("Search")); - button->SetStyle(Button::STYLE_NATIVE_TEXTBUTTON); + button->SetStyle(Button::STYLE_BUTTON); contents->AddChildView(button); button->SetBounds(112, 5, 60, 30); button->set_id(kSearchButtonID); @@ -549,12 +546,12 @@ void FocusTraversalTest::InitContentView() { contents->set_background(Background::CreateSolidBackground(SK_ColorBLUE)); contents->set_id(kThumbnailContainerID); button = new LabelButton(NULL, ASCIIToUTF16("Star")); - button->SetStyle(Button::STYLE_NATIVE_TEXTBUTTON); + button->SetStyle(Button::STYLE_BUTTON); contents->AddChildView(button); button->SetBounds(5, 5, 50, 30); button->set_id(kThumbnailStarID); button = new LabelButton(NULL, ASCIIToUTF16("SuperStar")); - button->SetStyle(Button::STYLE_NATIVE_TEXTBUTTON); + button->SetStyle(Button::STYLE_BUTTON); contents->AddChildView(button); button->SetBounds(60, 5, 100, 30); button->set_id(kThumbnailSuperStarID); @@ -579,9 +576,6 @@ TEST_F(FocusTraversalTest, NormalTraversal) { kSearchTextfieldID, kSearchButtonID, kHelpLinkID, kThumbnailContainerID, kThumbnailStarID, kThumbnailSuperStarID }; - // Uncomment the following line to manually test the UI of this test. - // base::RunLoop(new AcceleratorHandler()).Run(); - // Let's traverse the whole focus hierarchy (several times, to make sure it // loops OK). GetFocusManager()->ClearFocus(); @@ -631,9 +625,6 @@ TEST_F(FocusTraversalTest, TraversalWithNonEnabledViews) { v->SetEnabled(false); } - // Uncomment the following line to manually test the UI of this test. - // base::RunLoop(new AcceleratorHandler()).Run(); - View* focused_view; // Let's do one traversal (several times, to make sure it loops ok). GetFocusManager()->ClearFocus(); @@ -682,9 +673,6 @@ TEST_F(FocusTraversalTest, TraversalWithInvisibleViews) { v->SetVisible(false); } - // Uncomment the following line to manually test the UI of this test. - // base::RunLoop(new AcceleratorHandler()).Run(); - View* focused_view; // Let's do one traversal (several times, to make sure it loops ok). GetFocusManager()->ClearFocus(); diff --git a/chromium/ui/views/focus/view_storage.h b/chromium/ui/views/focus/view_storage.h index 629aec1cbb9..5e36f3c5e9b 100644 --- a/chromium/ui/views/focus/view_storage.h +++ b/chromium/ui/views/focus/view_storage.h @@ -17,7 +17,7 @@ template <typename T> struct DefaultSingletonTraits; // used for example in the FocusManager to store/restore focused views when the // main window becomes active/inactive. // It automatically removes a view from the storage if the view is removed from -// the tree hierarchy. +// the tree hierarchy or when the view is destroyed, which ever comes first. // // To use it, you first need to create a view storage id that can then be used // to store/retrieve views. diff --git a/chromium/ui/views/ime/OWNERS b/chromium/ui/views/ime/OWNERS index 60ba2b38ade..9f6409afd85 100644 --- a/chromium/ui/views/ime/OWNERS +++ b/chromium/ui/views/ime/OWNERS @@ -1,2 +1,6 @@ +# primary reviewer +yukishiino@chromium.org + +# backup reviewers. +mukai@chromium.org nona@chromium.org -penghuang@chromium.org diff --git a/chromium/ui/views/ime/input_method.h b/chromium/ui/views/ime/input_method.h index f4e8a482285..ffb5ae9c02f 100644 --- a/chromium/ui/views/ime/input_method.h +++ b/chromium/ui/views/ime/input_method.h @@ -34,7 +34,7 @@ class Widget; // designed to be bound to top-level Widgets. class VIEWS_EXPORT InputMethod { public: - // TODO(yukawa): Move these typedef into ime_constants.h or somewhere. + #if defined(OS_WIN) typedef LRESULT NativeEventResult; #else @@ -94,10 +94,6 @@ class VIEWS_EXPORT InputMethod { // tag, or an empty string if the input method cannot provide it. virtual std::string GetInputLocale() = 0; - // Returns the text direction of current keyboard layout or input method, or - // base::i18n::UNKNOWN_DIRECTION if the input method cannot provide it. - virtual base::i18n::TextDirection GetInputTextDirection() = 0; - // Returns true if the input method is ready to process keyboard events and // generate composition or text results. It is not necessary to notify // inactive input methods of caret bounds or text input type changes. @@ -118,6 +114,9 @@ class VIEWS_EXPORT InputMethod { // of IME popups is not supported. virtual bool IsCandidatePopupOpen() const = 0; + // Displays an on screen keyboard if enabled. + virtual void ShowImeIfNeeded() = 0; + // Returns true if the input method is a mock instance used for testing. virtual bool IsMock() const = 0; diff --git a/chromium/ui/views/ime/input_method_bridge.cc b/chromium/ui/views/ime/input_method_bridge.cc index f2e00ce03e1..97b040056ad 100644 --- a/chromium/ui/views/ime/input_method_bridge.cc +++ b/chromium/ui/views/ime/input_method_bridge.cc @@ -33,6 +33,7 @@ class InputMethodBridge::HostObserver : public ui::InputMethodObserver { const ui::TextInputClient* client) OVERRIDE {} virtual void OnInputMethodDestroyed( const ui::InputMethod* input_method) OVERRIDE; + virtual void OnShowImeIfNeeded() OVERRIDE {} private: InputMethodBridge* bridge_; @@ -159,12 +160,6 @@ std::string InputMethodBridge::GetInputLocale() { return host_->GetInputLocale(); } -base::i18n::TextDirection InputMethodBridge::GetInputTextDirection() { - DCHECK(host_); - - return host_->GetInputTextDirection(); -} - bool InputMethodBridge::IsActive() { DCHECK(host_); @@ -177,8 +172,13 @@ bool InputMethodBridge::IsCandidatePopupOpen() const { return host_->IsCandidatePopupOpen(); } +void InputMethodBridge::ShowImeIfNeeded() { + DCHECK(host_); + host_->ShowImeIfNeeded(); +} + // Overridden from TextInputClient. Forward an event from the system-wide IME -// to the text input |client|, which is e.g. views::NativeTextfieldViews. +// to the text input |client|, which is e.g. views::Textfield. void InputMethodBridge::SetCompositionText( const ui::CompositionText& composition) { TextInputClient* client = GetTextInputClient(); @@ -198,13 +198,13 @@ void InputMethodBridge::ClearCompositionText() { client->ClearCompositionText(); } -void InputMethodBridge::InsertText(const string16& text) { +void InputMethodBridge::InsertText(const base::string16& text) { TextInputClient* client = GetTextInputClient(); if (client) client->InsertText(text); } -void InputMethodBridge::InsertChar(char16 ch, int flags) { +void InputMethodBridge::InsertChar(base::char16 ch, int flags) { TextInputClient* client = GetTextInputClient(); if (client) client->InsertChar(ch, flags); @@ -280,7 +280,7 @@ bool InputMethodBridge::DeleteRange(const gfx::Range& range) { } bool InputMethodBridge::GetTextFromRange(const gfx::Range& range, - string16* text) const { + base::string16* text) const { TextInputClient* client = GetTextInputClient(); return client ? client->GetTextFromRange(range, text) : false; } @@ -319,6 +319,13 @@ void InputMethodBridge::OnCandidateWindowUpdated() { void InputMethodBridge::OnCandidateWindowHidden() { } +bool InputMethodBridge::IsEditingCommandEnabled(int command_id) { + return false; +} + +void InputMethodBridge::ExecuteEditingCommand(int command_id) { +} + // Overridden from FocusChangeListener. void InputMethodBridge::OnWillChangeFocus(View* focused_before, View* focused) { if (HasCompositionText()) { diff --git a/chromium/ui/views/ime/input_method_bridge.h b/chromium/ui/views/ime/input_method_bridge.h index 5e28a034b99..cdabecd7903 100644 --- a/chromium/ui/views/ime/input_method_bridge.h +++ b/chromium/ui/views/ime/input_method_bridge.h @@ -45,17 +45,17 @@ class InputMethodBridge : public InputMethodBase, virtual void CancelComposition(View* view) OVERRIDE; virtual void OnInputLocaleChanged() OVERRIDE; virtual std::string GetInputLocale() OVERRIDE; - virtual base::i18n::TextDirection GetInputTextDirection() OVERRIDE; virtual bool IsActive() OVERRIDE; virtual bool IsCandidatePopupOpen() const OVERRIDE; + virtual void ShowImeIfNeeded() OVERRIDE; // Overridden from TextInputClient: virtual void SetCompositionText( const ui::CompositionText& composition) OVERRIDE; virtual void ConfirmCompositionText() OVERRIDE; virtual void ClearCompositionText() OVERRIDE; - virtual void InsertText(const string16& text) OVERRIDE; - virtual void InsertChar(char16 ch, int flags) OVERRIDE; + virtual void InsertText(const base::string16& text) OVERRIDE; + virtual void InsertChar(base::char16 ch, int flags) OVERRIDE; virtual gfx::NativeWindow GetAttachedWindow() const OVERRIDE; virtual ui::TextInputType GetTextInputType() const OVERRIDE; virtual ui::TextInputMode GetTextInputMode() const OVERRIDE; @@ -70,7 +70,7 @@ class InputMethodBridge : public InputMethodBase, virtual bool SetSelectionRange(const gfx::Range& range) OVERRIDE; virtual bool DeleteRange(const gfx::Range& range) OVERRIDE; virtual bool GetTextFromRange(const gfx::Range& range, - string16* text) const OVERRIDE; + base::string16* text) const OVERRIDE; virtual void OnInputMethodChanged() OVERRIDE; virtual bool ChangeTextDirectionAndLayoutAlignment( base::i18n::TextDirection direction) OVERRIDE; @@ -79,6 +79,8 @@ class InputMethodBridge : public InputMethodBase, virtual void OnCandidateWindowShown() OVERRIDE; virtual void OnCandidateWindowUpdated() OVERRIDE; virtual void OnCandidateWindowHidden() OVERRIDE; + virtual bool IsEditingCommandEnabled(int command_id) OVERRIDE; + virtual void ExecuteEditingCommand(int command_id) OVERRIDE; // Overridden from FocusChangeListener. virtual void OnWillChangeFocus(View* focused_before, View* focused) OVERRIDE; diff --git a/chromium/ui/views/ime/mock_input_method.cc b/chromium/ui/views/ime/mock_input_method.cc index b04dafbfa63..ccc054db498 100644 --- a/chromium/ui/views/ime/mock_input_method.cc +++ b/chromium/ui/views/ime/mock_input_method.cc @@ -22,7 +22,6 @@ MockInputMethod::MockInputMethod() cancel_composition_called_(false), input_locale_changed_(false), locale_("en-US"), - direction_(base::i18n::LEFT_TO_RIGHT), active_(true) { } @@ -35,7 +34,6 @@ MockInputMethod::MockInputMethod(internal::InputMethodDelegate* delegate) cancel_composition_called_(false), input_locale_changed_(false), locale_("en-US"), - direction_(base::i18n::LEFT_TO_RIGHT), active_(true) { SetDelegate(delegate); } @@ -90,7 +88,7 @@ void MockInputMethod::DispatchKeyEvent(const ui::KeyEvent& key) { client->ClearCompositionText(); } } else if (key.type() == ui::ET_KEY_PRESSED) { - char16 ch = key.GetCharacter(); + base::char16 ch = key.GetCharacter(); client->InsertChar(ch, key.flags()); } } @@ -124,10 +122,6 @@ std::string MockInputMethod::GetInputLocale() { return locale_; } -base::i18n::TextDirection MockInputMethod::GetInputTextDirection() { - return direction_; -} - bool MockInputMethod::IsActive() { return active_; } @@ -136,6 +130,9 @@ bool MockInputMethod::IsCandidatePopupOpen() const { return false; } +void MockInputMethod::ShowImeIfNeeded() { +} + bool MockInputMethod::IsMock() const { return true; } @@ -159,7 +156,7 @@ void MockInputMethod::SetCompositionTextForNextKey( composition_ = composition; } -void MockInputMethod::SetResultTextForNextKey(const string16& result) { +void MockInputMethod::SetResultTextForNextKey(const base::string16& result) { result_text_ = result; } @@ -170,14 +167,6 @@ void MockInputMethod::SetInputLocale(const std::string& locale) { } } -void MockInputMethod::SetInputTextDirection( - base::i18n::TextDirection direction) { - if (direction_ != direction) { - direction_ = direction; - OnInputMethodChanged(); - } -} - void MockInputMethod::SetActive(bool active) { if (active_ != active) { active_ = active; diff --git a/chromium/ui/views/ime/mock_input_method.h b/chromium/ui/views/ime/mock_input_method.h index fad5315d5fb..013742cff1c 100644 --- a/chromium/ui/views/ime/mock_input_method.h +++ b/chromium/ui/views/ime/mock_input_method.h @@ -34,9 +34,9 @@ class VIEWS_EXPORT MockInputMethod : public InputMethodBase { virtual void CancelComposition(View* view) OVERRIDE; virtual void OnInputLocaleChanged() OVERRIDE; virtual std::string GetInputLocale() OVERRIDE; - virtual base::i18n::TextDirection GetInputTextDirection() OVERRIDE; virtual bool IsActive() OVERRIDE; virtual bool IsCandidatePopupOpen() const OVERRIDE; + virtual void ShowImeIfNeeded() OVERRIDE; virtual bool IsMock() const OVERRIDE; bool focus_changed() const { return focus_changed_; } @@ -52,10 +52,9 @@ class VIEWS_EXPORT MockInputMethod : public InputMethodBase { void Clear(); void SetCompositionTextForNextKey(const ui::CompositionText& composition); - void SetResultTextForNextKey(const string16& result); + void SetResultTextForNextKey(const base::string16& result); void SetInputLocale(const std::string& locale); - void SetInputTextDirection(base::i18n::TextDirection direction); void SetActive(bool active); private: @@ -75,7 +74,7 @@ class VIEWS_EXPORT MockInputMethod : public InputMethodBase { // Result text for the next key event. It'll be cleared automatically after // dispatching the next key event. - string16 result_text_; + base::string16 result_text_; // Record call state of corresponding methods. They will be set to false // automatically before dispatching a key event. @@ -88,7 +87,6 @@ class VIEWS_EXPORT MockInputMethod : public InputMethodBase { // To mock corresponding input method prooperties. std::string locale_; - base::i18n::TextDirection direction_; bool active_; DISALLOW_COPY_AND_ASSIGN(MockInputMethod); diff --git a/chromium/ui/views/ime/null_input_method.cc b/chromium/ui/views/ime/null_input_method.cc new file mode 100644 index 00000000000..666d6cfba39 --- /dev/null +++ b/chromium/ui/views/ime/null_input_method.cc @@ -0,0 +1,62 @@ +// Copyright 2014 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/ime/null_input_method.h" + +namespace views { + +NullInputMethod::NullInputMethod() {} + +void NullInputMethod::SetDelegate( + internal::InputMethodDelegate* /* delegate */) {} + +void NullInputMethod::Init(Widget* /* widget */) {} + +void NullInputMethod::OnFocus() {} + +void NullInputMethod::OnBlur() {} + +bool NullInputMethod::OnUntranslatedIMEMessage( + const base::NativeEvent& /* event */, + NativeEventResult* /* result */) { + return false; +} + +void NullInputMethod::DispatchKeyEvent(const ui::KeyEvent& /* key */) {} + +void NullInputMethod::OnTextInputTypeChanged(View* /* view */) {} + +void NullInputMethod::OnCaretBoundsChanged(View* /* view */) {} + +void NullInputMethod::CancelComposition(View* /* view */) {} + +void NullInputMethod::OnInputLocaleChanged() {} + +std::string NullInputMethod::GetInputLocale() { + return std::string(); +} + +bool NullInputMethod::IsActive() { + return false; +} + +ui::TextInputClient* NullInputMethod::GetTextInputClient() const { + return NULL; +} + +ui::TextInputType NullInputMethod::GetTextInputType() const { + return ui::TEXT_INPUT_TYPE_NONE; +} + +bool NullInputMethod::IsCandidatePopupOpen() const { + return false; +} + +void NullInputMethod::ShowImeIfNeeded() {} + +bool NullInputMethod::IsMock() const { + return false; +} + +} // namespace views diff --git a/chromium/ui/views/ime/null_input_method.h b/chromium/ui/views/ime/null_input_method.h new file mode 100644 index 00000000000..49234d3d551 --- /dev/null +++ b/chromium/ui/views/ime/null_input_method.h @@ -0,0 +1,50 @@ +// Copyright 2014 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_IME_NULL_INPUT_METHOD_H_ +#define UI_VIEWS_IME_NULL_INPUT_METHOD_H_ + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "ui/views/ime/input_method.h" + +namespace views { + +// An implementation of views::InputMethod which does nothing. +// +// We're working on removing views::InputMethod{,Base,Bridge} and going to use +// only ui::InputMethod. Use this class instead of views::InputMethodBridge +// with ui::TextInputFocusManager to effectively eliminate the +// views::InputMethod layer. +class NullInputMethod : public InputMethod { + public: + NullInputMethod(); + + // Overridden from InputMethod: + virtual void SetDelegate(internal::InputMethodDelegate* delegate) OVERRIDE; + virtual void Init(Widget* widget) OVERRIDE; + virtual void OnFocus() OVERRIDE; + virtual void OnBlur() OVERRIDE; + virtual bool OnUntranslatedIMEMessage(const base::NativeEvent& event, + NativeEventResult* result) OVERRIDE; + virtual void DispatchKeyEvent(const ui::KeyEvent& key) OVERRIDE; + virtual void OnTextInputTypeChanged(View* view) OVERRIDE; + virtual void OnCaretBoundsChanged(View* view) OVERRIDE; + virtual void CancelComposition(View* view) OVERRIDE; + virtual void OnInputLocaleChanged() OVERRIDE; + virtual std::string GetInputLocale() OVERRIDE; + virtual bool IsActive() OVERRIDE; + virtual ui::TextInputClient* GetTextInputClient() const OVERRIDE; + virtual ui::TextInputType GetTextInputType() const OVERRIDE; + virtual bool IsCandidatePopupOpen() const OVERRIDE; + virtual void ShowImeIfNeeded() OVERRIDE; + virtual bool IsMock() const OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(NullInputMethod); +}; + +} // namespace views + +#endif // UI_VIEWS_IME_NULL_INPUT_METHOD_H_ diff --git a/chromium/ui/views/layout/box_layout.cc b/chromium/ui/views/layout/box_layout.cc index c11479678a9..c33264c5eeb 100644 --- a/chromium/ui/views/layout/box_layout.cc +++ b/chromium/ui/views/layout/box_layout.cc @@ -19,7 +19,7 @@ BoxLayout::BoxLayout(BoxLayout::Orientation orientation, inside_border_vertical_spacing, inside_border_horizontal_spacing), between_child_spacing_(between_child_spacing), - spread_blank_space_(false) { + main_axis_alignment_(MAIN_AXIS_ALIGNMENT_START) { } BoxLayout::~BoxLayout() { @@ -29,38 +29,55 @@ void BoxLayout::Layout(View* host) { gfx::Rect child_area(host->GetLocalBounds()); child_area.Inset(host->GetInsets()); child_area.Inset(inside_border_insets_); - int x = child_area.x(); - int y = child_area.y(); int padding = 0; - if (spread_blank_space_) { - int total = 0; - int visible = 0; + if (main_axis_alignment_ != MAIN_AXIS_ALIGNMENT_START) { + int total_main_axis_size = 0; + int num_visible = 0; for (int i = 0; i < host->child_count(); ++i) { View* child = host->child_at(i); if (!child->visible()) continue; if (orientation_ == kHorizontal) { - total += child->GetPreferredSize().width() + between_child_spacing_; + total_main_axis_size += + child->GetPreferredSize().width() + between_child_spacing_; } else { - total += child->GetHeightForWidth(child_area.width()) + - between_child_spacing_; + total_main_axis_size += child->GetHeightForWidth(child_area.width()) + + between_child_spacing_; } - ++visible; + ++num_visible; } - if (visible) { - total -= between_child_spacing_; - if (orientation_ == kHorizontal) - padding = (child_area.width() - total) / visible; - else - padding = (child_area.height() - total) / visible; - - if (padding < 0) - padding = 0; + if (num_visible) { + total_main_axis_size -= between_child_spacing_; + int free_space = MainAxisSize(child_area) - total_main_axis_size; + int position = MainAxisPosition(child_area); + int size = MainAxisSize(child_area); + switch (main_axis_alignment_) { + case MAIN_AXIS_ALIGNMENT_FILL: + padding = std::max(free_space / num_visible, 0); + break; + case MAIN_AXIS_ALIGNMENT_CENTER: + position += free_space / 2; + size = total_main_axis_size; + break; + case MAIN_AXIS_ALIGNMENT_END: + position += free_space; + size = total_main_axis_size; + break; + default: + NOTREACHED(); + break; + } + gfx::Rect new_child_area(child_area); + SetMainAxisPosition(position, &new_child_area); + SetMainAxisSize(size, &new_child_area); + child_area.Intersect(new_child_area); } } + int x = child_area.x(); + int y = child_area.y(); for (int i = 0; i < host->child_count(); ++i) { View* child = host->child_at(i); if (child->visible()) { @@ -81,12 +98,12 @@ void BoxLayout::Layout(View* host) { } } -gfx::Size BoxLayout::GetPreferredSize(View* host) { +gfx::Size BoxLayout::GetPreferredSize(const View* host) const { // Calculate the child views' preferred width. int width = 0; if (orientation_ == kVertical) { for (int i = 0; i < host->child_count(); ++i) { - View* child = host->child_at(i); + const View* child = host->child_at(i); if (!child->visible()) continue; @@ -97,13 +114,35 @@ gfx::Size BoxLayout::GetPreferredSize(View* host) { return GetPreferredSizeForChildWidth(host, width); } -int BoxLayout::GetPreferredHeightForWidth(View* host, int width) { +int BoxLayout::GetPreferredHeightForWidth(const View* host, int width) const { int child_width = width - NonChildSize(host).width(); return GetPreferredSizeForChildWidth(host, child_width).height(); } -gfx::Size BoxLayout::GetPreferredSizeForChildWidth(View* host, - int child_area_width) { +int BoxLayout::MainAxisSize(const gfx::Rect& child_area) const { + return orientation_ == kHorizontal ? child_area.width() : child_area.height(); +} + +int BoxLayout::MainAxisPosition(const gfx::Rect& child_area) const { + return orientation_ == kHorizontal ? child_area.x() : child_area.y(); +} + +void BoxLayout::SetMainAxisSize(int size, gfx::Rect* child_area) const { + if (orientation_ == kHorizontal) + child_area->set_width(size); + else + child_area->set_height(size); +} + +void BoxLayout::SetMainAxisPosition(int position, gfx::Rect* child_area) const { + if (orientation_ == kHorizontal) + child_area->set_x(position); + else + child_area->set_y(position); +} + +gfx::Size BoxLayout::GetPreferredSizeForChildWidth(const View* host, + int child_area_width) const { gfx::Rect child_area_bounds; if (orientation_ == kHorizontal) { @@ -112,7 +151,7 @@ gfx::Size BoxLayout::GetPreferredSizeForChildWidth(View* host, // TODO(estade): fix this if it ever becomes a problem. int position = 0; for (int i = 0; i < host->child_count(); ++i) { - View* child = host->child_at(i); + const View* child = host->child_at(i); if (!child->visible()) continue; @@ -127,7 +166,7 @@ gfx::Size BoxLayout::GetPreferredSizeForChildWidth(View* host, } else { int height = 0; for (int i = 0; i < host->child_count(); ++i) { - View* child = host->child_at(i); + const View* child = host->child_at(i); if (!child->visible()) continue; @@ -147,7 +186,7 @@ gfx::Size BoxLayout::GetPreferredSizeForChildWidth(View* host, child_area_bounds.height() + non_child_size.height()); } -gfx::Size BoxLayout::NonChildSize(View* host) { +gfx::Size BoxLayout::NonChildSize(const View* host) const { gfx::Insets insets(host->GetInsets()); return gfx::Size(insets.width() + inside_border_insets_.width(), insets.height() + inside_border_insets_.height()); diff --git a/chromium/ui/views/layout/box_layout.h b/chromium/ui/views/layout/box_layout.h index 7670d8cb05e..e1d22ac91a2 100644 --- a/chromium/ui/views/layout/box_layout.h +++ b/chromium/ui/views/layout/box_layout.h @@ -11,6 +11,7 @@ #include "ui/views/layout/layout_manager.h" namespace gfx { +class Rect; class Size; } @@ -30,6 +31,25 @@ class VIEWS_EXPORT BoxLayout : public LayoutManager { kVertical, }; + // This specifies where along the main axis the children should be laid out. + // e.g. a horizontal layout of MAIN_AXIS_ALIGNMENT_END will result in the + // child views being right-aligned. + enum MainAxisAlignment { + MAIN_AXIS_ALIGNMENT_START, + MAIN_AXIS_ALIGNMENT_CENTER, + MAIN_AXIS_ALIGNMENT_END, + + // This distributes extra space among the child views. This increases the + // size of child views along the main axis rather than the space between + // them. + MAIN_AXIS_ALIGNMENT_FILL, + // TODO(calamity): Add MAIN_AXIS_ALIGNMENT_JUSTIFY which spreads blank space + // in-between the child views. + }; + + // TODO(calamity): Add CrossAxisAlignment property to allow cross axis + // alignment. + // Use |inside_border_horizontal_spacing| and // |inside_border_vertical_spacing| to add additional space between the child // view area and the host view border. |between_child_spacing| controls the @@ -40,22 +60,36 @@ class VIEWS_EXPORT BoxLayout : public LayoutManager { int between_child_spacing); virtual ~BoxLayout(); - void set_spread_blank_space(bool spread) { - spread_blank_space_ = spread; + void set_main_axis_alignment(MainAxisAlignment main_axis_alignment) { + main_axis_alignment_ = main_axis_alignment; + } + + void set_inside_border_insets(const gfx::Insets& insets) { + inside_border_insets_ = insets; } // Overridden from views::LayoutManager: virtual void Layout(View* host) OVERRIDE; - virtual gfx::Size GetPreferredSize(View* host) OVERRIDE; - virtual int GetPreferredHeightForWidth(View* host, int width) OVERRIDE; + virtual gfx::Size GetPreferredSize(const View* host) const OVERRIDE; + virtual int GetPreferredHeightForWidth(const View* host, + int width) const OVERRIDE; private: + // Returns the size and position along the main axis of |child_area|. + int MainAxisSize(const gfx::Rect& child_area) const; + int MainAxisPosition(const gfx::Rect& child_area) const; + + // Sets the size and position along the main axis of |child_area|. + void SetMainAxisSize(int size, gfx::Rect* child_area) const; + void SetMainAxisPosition(int position, gfx::Rect* child_area) const; + // The preferred size for the dialog given the width of the child area. - gfx::Size GetPreferredSizeForChildWidth(View* host, int child_area_width); + gfx::Size GetPreferredSizeForChildWidth(const View* host, + int child_area_width) const; // The amount of space the layout requires in addition to any space for the // child views. - gfx::Size NonChildSize(View* host); + gfx::Size NonChildSize(const View* host) const; const Orientation orientation_; @@ -65,9 +99,9 @@ class VIEWS_EXPORT BoxLayout : public LayoutManager { // Spacing to put in between child views. const int between_child_spacing_; - // Whether the available extra space should be distributed among the child - // views. - bool spread_blank_space_; + // The alignment of children in the main axis. This is + // MAIN_AXIS_ALIGNMENT_START by default. + MainAxisAlignment main_axis_alignment_; DISALLOW_IMPLICIT_CONSTRUCTORS(BoxLayout); }; diff --git a/chromium/ui/views/layout/box_layout_unittest.cc b/chromium/ui/views/layout/box_layout_unittest.cc index 2c12090685e..42f8f4272c6 100644 --- a/chromium/ui/views/layout/box_layout_unittest.cc +++ b/chromium/ui/views/layout/box_layout_unittest.cc @@ -55,6 +55,27 @@ TEST_F(BoxLayoutTest, AlignmentVertical) { EXPECT_EQ(gfx::Rect(0, 10, 20, 10), v2->bounds()); } +TEST_F(BoxLayoutTest, SetInsideBorderInsets) { + layout_.reset(new BoxLayout(BoxLayout::kHorizontal, 10, 20, 0)); + View* v1 = new StaticSizedView(gfx::Size(10, 20)); + host_->AddChildView(v1); + View* v2 = new StaticSizedView(gfx::Size(10, 10)); + host_->AddChildView(v2); + EXPECT_EQ(gfx::Size(40, 60), layout_->GetPreferredSize(host_.get())); + host_->SetBounds(0, 0, 40, 60); + layout_->Layout(host_.get()); + EXPECT_EQ(gfx::Rect(10, 20, 10, 20), v1->bounds()); + EXPECT_EQ(gfx::Rect(20, 20, 10, 20), v2->bounds()); + + layout_->set_inside_border_insets( + gfx::Insets(5, 10, 15, 20)); + EXPECT_EQ(gfx::Size(50, 40), layout_->GetPreferredSize(host_.get())); + host_->SetBounds(0, 0, 50, 40); + layout_->Layout(host_.get()); + EXPECT_EQ(gfx::Rect(10, 5, 10, 20), v1->bounds()); + EXPECT_EQ(gfx::Rect(20, 5, 10, 20), v2->bounds()); +} + TEST_F(BoxLayoutTest, Spacing) { layout_.reset(new BoxLayout(BoxLayout::kHorizontal, 7, 7, 8)); View* v1 = new StaticSizedView(gfx::Size(10, 20)); @@ -75,6 +96,25 @@ TEST_F(BoxLayoutTest, Overflow) { View* v2 = new StaticSizedView(gfx::Size(10, 20)); host_->AddChildView(v2); host_->SetBounds(0, 0, 10, 10); + + // Overflows by positioning views at the start and truncating anything that + // doesn't fit. + layout_->Layout(host_.get()); + EXPECT_EQ(gfx::Rect(0, 0, 10, 10), v1->bounds()); + EXPECT_EQ(gfx::Rect(0, 0, 0, 0), v2->bounds()); + + // All values of main axis alignment should overflow in the same manner. + layout_->set_main_axis_alignment(BoxLayout::MAIN_AXIS_ALIGNMENT_START); + layout_->Layout(host_.get()); + EXPECT_EQ(gfx::Rect(0, 0, 10, 10), v1->bounds()); + EXPECT_EQ(gfx::Rect(0, 0, 0, 0), v2->bounds()); + + layout_->set_main_axis_alignment(BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER); + layout_->Layout(host_.get()); + EXPECT_EQ(gfx::Rect(0, 0, 10, 10), v1->bounds()); + EXPECT_EQ(gfx::Rect(0, 0, 0, 0), v2->bounds()); + + layout_->set_main_axis_alignment(BoxLayout::MAIN_AXIS_ALIGNMENT_END); layout_->Layout(host_.get()); EXPECT_EQ(gfx::Rect(0, 0, 10, 10), v1->bounds()); EXPECT_EQ(gfx::Rect(0, 0, 0, 0), v2->bounds()); @@ -102,9 +142,9 @@ TEST_F(BoxLayoutTest, InvisibleChild) { EXPECT_EQ(gfx::Rect(10, 10, 10, 10), v2->bounds()); } -TEST_F(BoxLayoutTest, DistributeEmptySpace) { +TEST_F(BoxLayoutTest, MainAxisAlignmentFill) { layout_.reset(new BoxLayout(BoxLayout::kHorizontal, 10, 10, 10)); - layout_->set_spread_blank_space(true); + layout_->set_main_axis_alignment(BoxLayout::MAIN_AXIS_ALIGNMENT_FILL); View* v1 = new StaticSizedView(gfx::Size(20, 20)); host_->AddChildView(v1); @@ -156,4 +196,74 @@ TEST_F(BoxLayoutTest, EmptyPreferredSize) { } } +TEST_F(BoxLayoutTest, MainAxisAlignmentHorizontal) { + layout_.reset(new BoxLayout(BoxLayout::kHorizontal, 10, 10, 10)); + + View* v1 = new StaticSizedView(gfx::Size(20, 20)); + host_->AddChildView(v1); + View* v2 = new StaticSizedView(gfx::Size(10, 10)); + host_->AddChildView(v2); + + host_->SetBounds(0, 0, 100, 40); + + // Align children to the horizontal start by default. + layout_->Layout(host_.get()); + EXPECT_EQ(gfx::Rect(10, 10, 20, 20).ToString(), v1->bounds().ToString()); + EXPECT_EQ(gfx::Rect(40, 10, 10, 20).ToString(), v2->bounds().ToString()); + + // Ensure same results for MAIN_AXIS_ALIGNMENT_START. + layout_->set_main_axis_alignment(BoxLayout::MAIN_AXIS_ALIGNMENT_START); + layout_->Layout(host_.get()); + EXPECT_EQ(gfx::Rect(10, 10, 20, 20).ToString(), v1->bounds().ToString()); + EXPECT_EQ(gfx::Rect(40, 10, 10, 20).ToString(), v2->bounds().ToString()); + + // Aligns children to the center horizontally. + layout_->set_main_axis_alignment(BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER); + layout_->Layout(host_.get()); + EXPECT_EQ(gfx::Rect(30, 10, 20, 20).ToString(), v1->bounds().ToString()); + EXPECT_EQ(gfx::Rect(60, 10, 10, 20).ToString(), v2->bounds().ToString()); + + // Aligns children to the end of the host horizontally, accounting for the + // inside border spacing. + layout_->set_main_axis_alignment(BoxLayout::MAIN_AXIS_ALIGNMENT_END); + layout_->Layout(host_.get()); + EXPECT_EQ(gfx::Rect(50, 10, 20, 20).ToString(), v1->bounds().ToString()); + EXPECT_EQ(gfx::Rect(80, 10, 10, 20).ToString(), v2->bounds().ToString()); +} + +TEST_F(BoxLayoutTest, MainAxisAlignmentVertical) { + layout_.reset(new BoxLayout(BoxLayout::kVertical, 10, 10, 10)); + + View* v1 = new StaticSizedView(gfx::Size(20, 20)); + host_->AddChildView(v1); + View* v2 = new StaticSizedView(gfx::Size(10, 10)); + host_->AddChildView(v2); + + host_->SetBounds(0, 0, 40, 100); + + // Align children to the vertical start by default. + layout_->Layout(host_.get()); + EXPECT_EQ(gfx::Rect(10, 10, 20, 20).ToString(), v1->bounds().ToString()); + EXPECT_EQ(gfx::Rect(10, 40, 20, 10).ToString(), v2->bounds().ToString()); + + // Ensure same results for MAIN_AXIS_ALIGNMENT_START. + layout_->set_main_axis_alignment(BoxLayout::MAIN_AXIS_ALIGNMENT_START); + layout_->Layout(host_.get()); + EXPECT_EQ(gfx::Rect(10, 10, 20, 20).ToString(), v1->bounds().ToString()); + EXPECT_EQ(gfx::Rect(10, 40, 20, 10).ToString(), v2->bounds().ToString()); + + // Aligns children to the center vertically. + layout_->set_main_axis_alignment(BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER); + layout_->Layout(host_.get()); + EXPECT_EQ(gfx::Rect(10, 30, 20, 20).ToString(), v1->bounds().ToString()); + EXPECT_EQ(gfx::Rect(10, 60, 20, 10).ToString(), v2->bounds().ToString()); + + // Aligns children to the end of the host vertically, accounting for the + // inside border spacing. + layout_->set_main_axis_alignment(BoxLayout::MAIN_AXIS_ALIGNMENT_END); + layout_->Layout(host_.get()); + EXPECT_EQ(gfx::Rect(10, 50, 20, 20).ToString(), v1->bounds().ToString()); + EXPECT_EQ(gfx::Rect(10, 80, 20, 10).ToString(), v2->bounds().ToString()); +} + } // namespace views diff --git a/chromium/ui/views/layout/fill_layout.cc b/chromium/ui/views/layout/fill_layout.cc index 54893671f58..21375461210 100644 --- a/chromium/ui/views/layout/fill_layout.cc +++ b/chromium/ui/views/layout/fill_layout.cc @@ -20,7 +20,7 @@ void FillLayout::Layout(View* host) { frame_view->SetBoundsRect(host->GetContentsBounds()); } -gfx::Size FillLayout::GetPreferredSize(View* host) { +gfx::Size FillLayout::GetPreferredSize(const View* host) const { if (!host->has_children()) return gfx::Size(); DCHECK_EQ(1, host->child_count()); @@ -29,7 +29,7 @@ gfx::Size FillLayout::GetPreferredSize(View* host) { return rect.size(); } -int FillLayout::GetPreferredHeightForWidth(View* host, int width) { +int FillLayout::GetPreferredHeightForWidth(const View* host, int width) const { if (!host->has_children()) return 0; DCHECK_EQ(1, host->child_count()); diff --git a/chromium/ui/views/layout/fill_layout.h b/chromium/ui/views/layout/fill_layout.h index 67c7f831457..f50bde0c6c7 100644 --- a/chromium/ui/views/layout/fill_layout.h +++ b/chromium/ui/views/layout/fill_layout.h @@ -25,8 +25,9 @@ class VIEWS_EXPORT FillLayout : public LayoutManager { // Overridden from LayoutManager: virtual void Layout(View* host) OVERRIDE; - virtual gfx::Size GetPreferredSize(View* host) OVERRIDE; - virtual int GetPreferredHeightForWidth(View* host, int width) OVERRIDE; + virtual gfx::Size GetPreferredSize(const View* host) const OVERRIDE; + virtual int GetPreferredHeightForWidth(const View* host, + int width) const OVERRIDE; private: DISALLOW_COPY_AND_ASSIGN(FillLayout); diff --git a/chromium/ui/views/layout/grid_layout.cc b/chromium/ui/views/layout/grid_layout.cc index e4bf19d47c8..7ab81083c34 100644 --- a/chromium/ui/views/layout/grid_layout.cc +++ b/chromium/ui/views/layout/grid_layout.cc @@ -57,7 +57,8 @@ class LayoutElement { for (typename std::vector<T*>::iterator i = elements->begin(); i != elements->end(); ++i) { total_percent += (*i)->ResizePercent(); - resize_count++; + if ((*i)->ResizePercent() > 0) + resize_count++; } if (total_percent == 0) { // None of the elements are resizable, return. @@ -161,7 +162,6 @@ class Column : public LayoutElement { GridLayout::SizeType size_type, int fixed_width, int min_width, - size_t index, bool is_padding) : LayoutElement(resize_percent), h_align_(h_align), @@ -170,7 +170,6 @@ class Column : public LayoutElement { same_size_column_(-1), fixed_width_(fixed_width), min_width_(min_width), - index_(index), is_padding_(is_padding), master_column_(NULL) {} @@ -200,9 +199,6 @@ class Column : public LayoutElement { const int fixed_width_; const int min_width_; - // Index of this column in the ColumnSet. - const size_t index_; - const bool is_padding_; // If multiple columns have their sizes linked, one is the @@ -263,10 +259,8 @@ void Column::AdjustSize(int size) { class Row : public LayoutElement { public: - Row(bool fixed_height, int height, float resize_percent, - ColumnSet* column_set) + Row(int height, float resize_percent, ColumnSet* column_set) : LayoutElement(resize_percent), - fixed_height_(fixed_height), height_(height), column_set_(column_set), max_ascent_(0), @@ -300,7 +294,6 @@ class Row : public LayoutElement { } private: - const bool fixed_height_; const int height_; // The column set used for this row; null for padding rows. ColumnSet* column_set_; @@ -421,17 +414,16 @@ void ColumnSet::AddColumn(GridLayout::Alignment h_align, int min_width, bool is_padding) { Column* column = new Column(h_align, v_align, resize_percent, size_type, - fixed_width, min_width, columns_.size(), - is_padding); + fixed_width, min_width, is_padding); columns_.push_back(column); } void ColumnSet::AddViewState(ViewState* view_state) { // view_states are ordered by column_span (in ascending order). - std::vector<ViewState*>::iterator i = lower_bound(view_states_.begin(), - view_states_.end(), - view_state, - CompareByColumnSpan); + std::vector<ViewState*>::iterator i = std::lower_bound(view_states_.begin(), + view_states_.end(), + view_state, + CompareByColumnSpan); view_states_.insert(i, view_state); } @@ -503,8 +495,8 @@ void ColumnSet::AccumulateMasterColumns() { Column* column = *i; Column* master_column = column->GetLastMasterColumn(); if (master_column && - find(master_columns_.begin(), master_columns_.end(), - master_column) == master_columns_.end()) { + std::find(master_columns_.begin(), master_columns_.end(), + master_column) == master_columns_.end()) { master_columns_.push_back(master_column); } // At this point, GetLastMasterColumn may not == master_column @@ -717,11 +709,11 @@ void GridLayout::StartRowWithPadding(float vertical_resize, int column_set_id, void GridLayout::StartRow(float vertical_resize, int column_set_id) { ColumnSet* column_set = GetColumnSet(column_set_id); DCHECK(column_set); - AddRow(new Row(false, 0, vertical_resize, column_set)); + AddRow(new Row(0, vertical_resize, column_set)); } void GridLayout::AddPaddingRow(float vertical_resize, int pixel_count) { - AddRow(new Row(true, pixel_count, vertical_resize, NULL)); + AddRow(new Row(pixel_count, vertical_resize, NULL)); } void GridLayout::SkipColumns(int col_count) { @@ -836,7 +828,7 @@ void GridLayout::Layout(View* host) { } } -gfx::Size GridLayout::GetPreferredSize(View* host) { +gfx::Size GridLayout::GetPreferredSize(const View* host) const { DCHECK(host_ == host); gfx::Size out; SizeRowsAndColumns(false, 0, 0, &out); @@ -845,7 +837,7 @@ gfx::Size GridLayout::GetPreferredSize(View* host) { return out; } -int GridLayout::GetPreferredHeightForWidth(View* host, int width) { +int GridLayout::GetPreferredHeightForWidth(const View* host, int width) const { DCHECK(host_ == host); gfx::Size pref; SizeRowsAndColumns(false, width, 0, &pref); @@ -853,7 +845,7 @@ int GridLayout::GetPreferredHeightForWidth(View* host, int width) { } void GridLayout::SizeRowsAndColumns(bool layout, int width, int height, - gfx::Size* pref) { + gfx::Size* pref) const { // Make sure the master columns have been calculated. CalculateMasterColumnsIfNecessary(); pref->SetSize(0, 0); @@ -958,7 +950,7 @@ void GridLayout::SizeRowsAndColumns(bool layout, int width, int height, } } -void GridLayout::CalculateMasterColumnsIfNecessary() { +void GridLayout::CalculateMasterColumnsIfNecessary() const { if (!calculated_master_columns_) { calculated_master_columns_ = true; for (std::vector<ColumnSet*>::iterator i = column_sets_.begin(); @@ -980,10 +972,10 @@ void GridLayout::AddViewState(ViewState* view_state) { next_column_ += view_state->col_span; current_row_col_set_->AddViewState(view_state); // view_states are ordered by row_span (in ascending order). - std::vector<ViewState*>::iterator i = lower_bound(view_states_.begin(), - view_states_.end(), - view_state, - CompareByRowSpan); + std::vector<ViewState*>::iterator i = std::lower_bound(view_states_.begin(), + view_states_.end(), + view_state, + CompareByRowSpan); view_states_.insert(i, view_state); SkipPaddingColumns(); } @@ -1003,14 +995,14 @@ void GridLayout::AddRow(Row* row) { SkipPaddingColumns(); } -void GridLayout::UpdateRemainingHeightFromRows(ViewState* view_state) { +void GridLayout::UpdateRemainingHeightFromRows(ViewState* view_state) const { for (int i = 0, start_row = view_state->start_row; i < view_state->row_span; ++i) { view_state->remaining_height -= rows_[i + start_row]->Size(); } } -void GridLayout::DistributeRemainingHeight(ViewState* view_state) { +void GridLayout::DistributeRemainingHeight(ViewState* view_state) const { int height = view_state->remaining_height; if (height <= 0) return; diff --git a/chromium/ui/views/layout/grid_layout.h b/chromium/ui/views/layout/grid_layout.h index f026eb7915f..ea578c8ad57 100644 --- a/chromium/ui/views/layout/grid_layout.h +++ b/chromium/ui/views/layout/grid_layout.h @@ -178,9 +178,10 @@ class VIEWS_EXPORT GridLayout : public LayoutManager { virtual void Layout(View* host) OVERRIDE; // Returns the preferred size for the GridLayout. - virtual gfx::Size GetPreferredSize(View* host) OVERRIDE; + virtual gfx::Size GetPreferredSize(const View* host) const OVERRIDE; - virtual int GetPreferredHeightForWidth(View* host, int width) OVERRIDE; + virtual int GetPreferredHeightForWidth(const View* host, + int width) const OVERRIDE; void set_minimum_size(const gfx::Size& size) { minimum_size_ = size; } @@ -189,11 +190,14 @@ class VIEWS_EXPORT GridLayout : public LayoutManager { // they both call into this method. This sizes the Columns/Rows as // appropriate. If layout is true, width/height give the width/height the // of the host, otherwise they are ignored. - void SizeRowsAndColumns(bool layout, int width, int height, gfx::Size* pref); + void SizeRowsAndColumns(bool layout, + int width, + int height, + gfx::Size* pref) const; // Calculates the master columns of all the column sets. See Column for // a description of what a master column is. - void CalculateMasterColumnsIfNecessary(); + void CalculateMasterColumnsIfNecessary() const; // This is called internally from AddView. It adds the ViewState to the // appropriate structures, and updates internal fields such as next_column_. @@ -205,12 +209,12 @@ class VIEWS_EXPORT GridLayout : public LayoutManager { // As the name says, updates the remaining_height of the ViewState for // all Rows the supplied ViewState touches. - void UpdateRemainingHeightFromRows(ViewState* state); + void UpdateRemainingHeightFromRows(ViewState* state) const; // If the view state's remaining height is > 0, it is distributed among // the rows the view state touches. This is used during layout to make // sure the Rows can accommodate a view. - void DistributeRemainingHeight(ViewState* state); + void DistributeRemainingHeight(ViewState* state) const; // Advances next_column_ past any padding columns. void SkipPaddingColumns(); @@ -222,7 +226,7 @@ class VIEWS_EXPORT GridLayout : public LayoutManager { View* const host_; // Whether or not we've calculated the master/linked columns. - bool calculated_master_columns_; + mutable bool calculated_master_columns_; // Used to verify a view isn't added with a row span that expands into // another column structure. @@ -244,13 +248,13 @@ class VIEWS_EXPORT GridLayout : public LayoutManager { bool adding_view_; // ViewStates. This is ordered by row_span in ascending order. - std::vector<ViewState*> view_states_; + mutable std::vector<ViewState*> view_states_; // ColumnSets. - std::vector<ColumnSet*> column_sets_; + mutable std::vector<ColumnSet*> column_sets_; // Rows. - std::vector<Row*> rows_; + mutable std::vector<Row*> rows_; // Minimum preferred size. gfx::Size minimum_size_; @@ -267,10 +271,11 @@ class VIEWS_EXPORT ColumnSet { // Adds a column for padding. When adding views, padding columns are // automatically skipped. For example, if you create a column set with - // two columns separated by a padding column, the first AddView automatically + // two columns separated by a padding column, the second AddView automatically // skips past the padding column. That is, to add two views, do: // layout->AddView(v1); layout->AddView(v2);, not: // layout->AddView(v1); layout->SkipColumns(1); layout->AddView(v2); + // See class description for details on |resize_percent|. void AddPaddingColumn(float resize_percent, int width); // Adds a column. The alignment gives the default alignment for views added @@ -282,6 +287,7 @@ class VIEWS_EXPORT ColumnSet { // made as wide as the widest views in each column, even if extra space is // provided. In other words, GridLayout does not automatically resize views // unless the column is marked as resizable. + // See class description for details on |resize_percent|. void AddColumn(GridLayout::Alignment h_align, GridLayout::Alignment v_align, float resize_percent, diff --git a/chromium/ui/views/layout/grid_layout_unittest.cc b/chromium/ui/views/layout/grid_layout_unittest.cc index 71988d870b6..ea7d231f04b 100644 --- a/chromium/ui/views/layout/grid_layout_unittest.cc +++ b/chromium/ui/views/layout/grid_layout_unittest.cc @@ -24,7 +24,7 @@ class SettableSizeView : public View { pref_ = pref; } - virtual gfx::Size GetPreferredSize() OVERRIDE { + virtual gfx::Size GetPreferredSize() const OVERRIDE { return pref_; } @@ -39,11 +39,11 @@ class FlexibleView : public View { circumference_ = circumference; } - virtual gfx::Size GetPreferredSize() OVERRIDE { + virtual gfx::Size GetPreferredSize() const OVERRIDE { return gfx::Size(0, circumference_ / 2); } - virtual int GetHeightForWidth(int width) OVERRIDE { + virtual int GetHeightForWidth(int width) const OVERRIDE { return std::max(0, circumference_ / 2 - width); } @@ -346,6 +346,33 @@ TEST_F(GridLayoutTest, HorizontalResizeTest2) { RemoveAll(); } +// Tests that space leftover due to rounding is distributed to the last +// resizable column. +TEST_F(GridLayoutTest, HorizontalResizeTest3) { + SettableSizeView v1(gfx::Size(10, 10)); + SettableSizeView v2(gfx::Size(10, 10)); + SettableSizeView v3(gfx::Size(10, 10)); + ColumnSet* c1 = layout.AddColumnSet(0); + c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, + 1, GridLayout::USE_PREF, 0, 0); + c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, + 1, GridLayout::USE_PREF, 0, 0); + c1->AddColumn(GridLayout::TRAILING, GridLayout::LEADING, + 0, GridLayout::USE_PREF, 0, 0); + layout.StartRow(0, 0); + layout.AddView(&v1); + layout.AddView(&v2); + layout.AddView(&v3); + + host.SetBounds(0, 0, 31, 10); + layout.Layout(&host); + ExpectViewBoundsEquals(0, 0, 10, 10, &v1); + ExpectViewBoundsEquals(10, 0, 11, 10, &v2); + ExpectViewBoundsEquals(21, 0, 10, 10, &v3); + + RemoveAll(); +} + TEST_F(GridLayoutTest, TestVerticalResize1) { SettableSizeView v1(gfx::Size(50, 20)); SettableSizeView v2(gfx::Size(10, 10)); diff --git a/chromium/ui/views/layout/layout_manager.cc b/chromium/ui/views/layout/layout_manager.cc index 188cc6f14fc..9a79ce75832 100644 --- a/chromium/ui/views/layout/layout_manager.cc +++ b/chromium/ui/views/layout/layout_manager.cc @@ -17,7 +17,8 @@ void LayoutManager::Installed(View* host) { void LayoutManager::Uninstalled(View* host) { } -int LayoutManager::GetPreferredHeightForWidth(View* host, int width) { +int LayoutManager::GetPreferredHeightForWidth(const View* host, + int width) const { return GetPreferredSize(host).height(); } diff --git a/chromium/ui/views/layout/layout_manager.h b/chromium/ui/views/layout/layout_manager.h index f678e721179..d10e09ed381 100644 --- a/chromium/ui/views/layout/layout_manager.h +++ b/chromium/ui/views/layout/layout_manager.h @@ -42,11 +42,11 @@ class VIEWS_EXPORT LayoutManager { // Return the preferred size which is the size required to give each // children their respective preferred size. - virtual gfx::Size GetPreferredSize(View* host) = 0; + virtual gfx::Size GetPreferredSize(const View* host) const = 0; // Returns the preferred height for the specified width. The default // implementation returns the value from GetPreferredSize. - virtual int GetPreferredHeightForWidth(View* host, int width); + virtual int GetPreferredHeightForWidth(const View* host, int width) const; // Notification that a view has been added. virtual void ViewAdded(View* host, View* view); diff --git a/chromium/ui/views/linux_ui/linux_ui.cc b/chromium/ui/views/linux_ui/linux_ui.cc index 6a35040f4f8..63c74020c19 100644 --- a/chromium/ui/views/linux_ui/linux_ui.cc +++ b/chromium/ui/views/linux_ui/linux_ui.cc @@ -5,6 +5,7 @@ #include "ui/views/linux_ui/linux_ui.h" #include "ui/base/ime/linux/linux_input_method_context_factory.h" +#include "ui/gfx/linux_font_delegate.h" #include "ui/shell_dialogs/linux_shell_dialog.h" namespace { @@ -19,7 +20,9 @@ void LinuxUI::SetInstance(LinuxUI* instance) { delete g_linux_ui; g_linux_ui = instance; LinuxInputMethodContextFactory::SetInstance(instance); + LinuxFontDelegate::SetInstance(instance); LinuxShellDialog::SetInstance(instance); + ui::SetTextEditKeyBindingsDelegate(instance); } LinuxUI* LinuxUI::instance() { diff --git a/chromium/ui/views/linux_ui/linux_ui.gyp b/chromium/ui/views/linux_ui/linux_ui.gyp index 1f87a1a01ee..5ba78e52baf 100644 --- a/chromium/ui/views/linux_ui/linux_ui.gyp +++ b/chromium/ui/views/linux_ui/linux_ui.gyp @@ -14,9 +14,9 @@ '../../base/base.gyp:base', '../../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', '../../skia/skia.gyp:skia', + '../base/ui_base.gyp:ui_base', '../native_theme/native_theme.gyp:native_theme', '../resources/ui_resources.gyp:ui_resources', - '../ui.gyp:ui', ], 'defines': [ 'LINUX_UI_IMPLEMENTATION', diff --git a/chromium/ui/views/linux_ui/linux_ui.h b/chromium/ui/views/linux_ui/linux_ui.h index 152b364282d..b5466f57ef5 100644 --- a/chromium/ui/views/linux_ui/linux_ui.h +++ b/chromium/ui/views/linux_ui/linux_ui.h @@ -5,15 +5,25 @@ #ifndef UI_VIEWS_LINUX_UI_LINUX_UI_H_ #define UI_VIEWS_LINUX_UI_LINUX_UI_H_ +#include <string> + +#include "base/callback.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/base/ime/linux/linux_input_method_context_factory.h" +#include "ui/events/linux/text_edit_key_bindings_delegate_auralinux.h" +#include "ui/gfx/linux_font_delegate.h" #include "ui/shell_dialogs/linux_shell_dialog.h" +#include "ui/views/controls/button/button.h" #include "ui/views/linux_ui/status_icon_linux.h" #include "ui/views/views_export.h" // The main entrypoint into Linux toolkit specific code. GTK code should only // be executed behind this interface. +namespace aura { +class Window; +} + namespace gfx { class Image; } @@ -23,6 +33,10 @@ class NativeTheme; } namespace views { +class Border; +class LabelButton; +class LabelButtonBorder; +class View; class WindowButtonOrderObserver; // Adapter class with targets to render like different toolkits. Set by any @@ -33,8 +47,22 @@ class WindowButtonOrderObserver; // complex method that pokes around with dlopen against a libuigtk2.so, a // liuigtk3.so, etc. class VIEWS_EXPORT LinuxUI : public ui::LinuxInputMethodContextFactory, - public ui::LinuxShellDialog { + public gfx::LinuxFontDelegate, + public ui::LinuxShellDialog, + public ui::TextEditKeyBindingsDelegateAuraLinux { public: + // Describes the window management actions that could be taken in response to + // a middle click in the non client area. + enum NonClientMiddleClickAction { + MIDDLE_CLICK_ACTION_NONE, + MIDDLE_CLICK_ACTION_LOWER, + MIDDLE_CLICK_ACTION_MINIMIZE, + MIDDLE_CLICK_ACTION_TOGGLE_MAXIMIZE + }; + + typedef base::Callback<ui::NativeTheme*(aura::Window* window)> + NativeThemeGetter; + virtual ~LinuxUI() {} // Sets the dynamically loaded singleton that draws the desktop native UI. @@ -67,9 +95,10 @@ class VIEWS_EXPORT LinuxUI : public ui::LinuxInputMethodContextFactory, // Returns a NativeTheme that will provide system colors and draw system // style widgets. - virtual ui::NativeTheme* GetNativeTheme() const = 0; + virtual ui::NativeTheme* GetNativeTheme(aura::Window* window) const = 0; - virtual void SetUseSystemTheme(bool use_system_theme) = 0; + // Used to set an override NativeTheme. + virtual void SetNativeThemeOverride(const NativeThemeGetter& callback) = 0; // Returns whether we should be using the native theme provided by this // object by default. @@ -86,7 +115,7 @@ class VIEWS_EXPORT LinuxUI : public ui::LinuxInputMethodContextFactory, // Create a native status icon. virtual scoped_ptr<StatusIconLinux> CreateLinuxStatusIcon( const gfx::ImageSkia& image, - const string16& tool_tip) const = 0; + const base::string16& tool_tip) const = 0; // Returns the icon for a given content type from the icon theme. // TODO(davidben): Add an observer for the theme changing, so we can drop the @@ -94,6 +123,11 @@ class VIEWS_EXPORT LinuxUI : public ui::LinuxInputMethodContextFactory, virtual gfx::Image GetIconForContentType( const std::string& content_type, int size) const = 0; + // Builds a Border which paints the native button style. + virtual scoped_ptr<Border> CreateNativeBorder( + views::LabelButton* owning_button, + scoped_ptr<views::LabelButtonBorder> border) = 0; + // Notifies the observer about changes about how window buttons should be // laid out. If the order is anything other than the default min,max,close on // the right, will immediately send a button change event to the observer. @@ -106,6 +140,16 @@ class VIEWS_EXPORT LinuxUI : public ui::LinuxInputMethodContextFactory, // Determines whether the user's window manager is Unity. virtual bool UnityIsRunning() = 0; + + // What action we should take when the user middle clicks on non-client + // area. The default is lowering the window. + virtual NonClientMiddleClickAction GetNonClientMiddleClickAction() = 0; + + // Notifies the window manager that start up has completed. + // Normally Chromium opens a new window on startup and GTK does this + // automatically. In case Chromium does not open a new window on startup, + // e.g. an existing browser window already exists, this should be called. + virtual void NotifyWindowManagerStartupComplete() = 0; }; } // namespace views diff --git a/chromium/ui/views/linux_ui/status_icon_linux.h b/chromium/ui/views/linux_ui/status_icon_linux.h index 5ed8be21a7e..b7ed3c72295 100644 --- a/chromium/ui/views/linux_ui/status_icon_linux.h +++ b/chromium/ui/views/linux_ui/status_icon_linux.h @@ -38,7 +38,7 @@ class VIEWS_EXPORT StatusIconLinux { virtual void SetImage(const gfx::ImageSkia& image) = 0; virtual void SetPressedImage(const gfx::ImageSkia& image) = 0; - virtual void SetToolTip(const string16& tool_tip) = 0; + virtual void SetToolTip(const base::string16& tool_tip) = 0; // Invoked after a call to SetContextMenu() to let the platform-specific // subclass update the native context menu based on the new model. The diff --git a/chromium/ui/views/linux_ui/window_button_order_provider.cc b/chromium/ui/views/linux_ui/window_button_order_provider.cc new file mode 100644 index 00000000000..903247883b8 --- /dev/null +++ b/chromium/ui/views/linux_ui/window_button_order_provider.cc @@ -0,0 +1,83 @@ +// Copyright 2014 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/window/window_button_order_provider.h" + +#include "ui/views/linux_ui/linux_ui.h" +#include "ui/views/linux_ui/window_button_order_observer.h" + +namespace views { + +namespace { + +class WindowButtonOrderObserverDelegate : public WindowButtonOrderProvider, + public WindowButtonOrderObserver { + public: + WindowButtonOrderObserverDelegate(); + virtual ~WindowButtonOrderObserverDelegate(); + + // WindowButtonOrderObserver: + virtual void OnWindowButtonOrderingChange( + const std::vector<views::FrameButton>& leading_buttons, + const std::vector<views::FrameButton>& trailing_buttons) OVERRIDE; + private: + DISALLOW_COPY_AND_ASSIGN(WindowButtonOrderObserverDelegate); +}; + +/////////////////////////////////////////////////////////////////////////////// +// WindowButtonOrderObserverDelegate, public: + +WindowButtonOrderObserverDelegate::WindowButtonOrderObserverDelegate() { + views::LinuxUI* ui = views::LinuxUI::instance(); + if (ui) + ui->AddWindowButtonOrderObserver(this); +} + +WindowButtonOrderObserverDelegate::~WindowButtonOrderObserverDelegate() { + views::LinuxUI* ui = views::LinuxUI::instance(); + if (ui) + ui->RemoveWindowButtonOrderObserver(this); +} + +void WindowButtonOrderObserverDelegate::OnWindowButtonOrderingChange( + const std::vector<views::FrameButton>& leading_buttons, + const std::vector<views::FrameButton>& trailing_buttons) { + SetWindowButtonOrder(leading_buttons, trailing_buttons); +} + +} // namespace + +// static +WindowButtonOrderProvider* WindowButtonOrderProvider::instance_ = NULL; + +/////////////////////////////////////////////////////////////////////////////// +// WindowButtonOrderProvider, public: + +// static +WindowButtonOrderProvider* WindowButtonOrderProvider::GetInstance() { + if (!instance_) + instance_ = new WindowButtonOrderObserverDelegate; + return instance_; +} + +/////////////////////////////////////////////////////////////////////////////// +// WindowButtonOrderProvider, protected: + +WindowButtonOrderProvider::WindowButtonOrderProvider() { + trailing_buttons_.push_back(views::FRAME_BUTTON_MINIMIZE); + trailing_buttons_.push_back(views::FRAME_BUTTON_MAXIMIZE); + trailing_buttons_.push_back(views::FRAME_BUTTON_CLOSE); +} + +WindowButtonOrderProvider::~WindowButtonOrderProvider() { +} + +void WindowButtonOrderProvider::SetWindowButtonOrder( + const std::vector<views::FrameButton>& leading_buttons, + const std::vector<views::FrameButton>& trailing_buttons) { + leading_buttons_ = leading_buttons; + trailing_buttons_ = trailing_buttons; +} + +} // namespace views diff --git a/chromium/ui/views/masked_view_targeter.cc b/chromium/ui/views/masked_view_targeter.cc new file mode 100644 index 00000000000..112df676808 --- /dev/null +++ b/chromium/ui/views/masked_view_targeter.cc @@ -0,0 +1,46 @@ +// Copyright 2014 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/masked_view_targeter.h" + +#include "ui/gfx/path.h" +#include "ui/gfx/skia_util.h" +#include "ui/views/view.h" + +namespace views { + +MaskedViewTargeter::MaskedViewTargeter(View* masked_view) + : masked_view_(masked_view) { +} + +MaskedViewTargeter::~MaskedViewTargeter() { +} + +bool MaskedViewTargeter::EventLocationInsideBounds( + ui::EventTarget* target, + const ui::LocatedEvent& event) const { + View* view = static_cast<View*>(target); + if (view == masked_view_) { + gfx::Path mask; + if (!GetHitTestMask(view, &mask)) + return ViewTargeter::EventLocationInsideBounds(view, event); + + gfx::Size size = view->bounds().size(); + SkRegion clip_region; + clip_region.setRect(0, 0, size.width(), size.height()); + + gfx::RectF bounds_f = ViewTargeter::BoundsForEvent(event); + if (view->parent()) + View::ConvertRectToTarget(view->parent(), view, &bounds_f); + gfx::Rect bounds = gfx::ToEnclosingRect(bounds_f); + + SkRegion mask_region; + return mask_region.setPath(mask, clip_region) && + mask_region.intersects(RectToSkIRect(bounds)); + } + + return ViewTargeter::EventLocationInsideBounds(view, event); +} + +} // namespace views diff --git a/chromium/ui/views/masked_view_targeter.h b/chromium/ui/views/masked_view_targeter.h new file mode 100644 index 00000000000..df155b915de --- /dev/null +++ b/chromium/ui/views/masked_view_targeter.h @@ -0,0 +1,46 @@ +// Copyright 2014 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_MASKED_VIEW_TARGETER_H_ +#define UI_VIEWS_MASKED_VIEW_TARGETER_H_ + +#include "ui/views/view_targeter.h" +#include "ui/views/views_export.h" + +namespace gfx { +class Path; +} + +namespace views { + +// Derived classes of MaskedViewTargeter are used to define custom-shaped +// hit test regions for a View used in event targeting. +// TODO(tdanderson|sadrul): Some refactoring opportunities may be possible +// between this class and MaskedWindowTargeter. +class VIEWS_EXPORT MaskedViewTargeter : public ViewTargeter { + public: + explicit MaskedViewTargeter(View* masked_view); + virtual ~MaskedViewTargeter(); + + // Sets the hit-test mask for |view| in |mask| (in |view|'s local + // coordinate system). Returns whether a valid mask has been set in |mask|. + virtual bool GetHitTestMask(const View* view, gfx::Path* mask) const = 0; + + protected: + const View* masked_view() const { return masked_view_; } + + // ui::EventTargeter: + virtual bool EventLocationInsideBounds( + ui::EventTarget* target, + const ui::LocatedEvent& event) const OVERRIDE; + + private: + View* masked_view_; + + DISALLOW_COPY_AND_ASSIGN(MaskedViewTargeter); +}; + +} // namespace views + +#endif // UI_VIEWS_MASKED_VIEW_TARGETER_H_ diff --git a/chromium/ui/views/metrics_aura.cc b/chromium/ui/views/metrics_aura.cc index 03aa832f01e..046baa892ca 100644 --- a/chromium/ui/views/metrics_aura.cc +++ b/chromium/ui/views/metrics_aura.cc @@ -11,7 +11,6 @@ namespace { // Default double click interval in milliseconds. -// Same as what gtk uses. const int kDefaultDoubleClickInterval = 500; } // namespace diff --git a/chromium/ui/views/metrics_mac.cc b/chromium/ui/views/metrics_mac.cc new file mode 100644 index 00000000000..3a399d19463 --- /dev/null +++ b/chromium/ui/views/metrics_mac.cc @@ -0,0 +1,25 @@ +// Copyright 2014 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/metrics.h" + +namespace { + +// Default double click interval in milliseconds. +// Same as what gtk uses. +const int kDefaultDoubleClickInterval = 500; + +} // namespace + +namespace views { + +int GetDoubleClickInterval() { + return kDefaultDoubleClickInterval; +} + +int GetMenuShowDelay() { + return 0; +} + +} // namespace views diff --git a/chromium/ui/views/metrics_win.cc b/chromium/ui/views/metrics_win.cc deleted file mode 100644 index 3e04446b3eb..00000000000 --- a/chromium/ui/views/metrics_win.cc +++ /dev/null @@ -1,22 +0,0 @@ -// 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/metrics.h" - -#include <windows.h> - -namespace views { - -int GetDoubleClickInterval() { - return ::GetDoubleClickTime(); -} - -int GetMenuShowDelay() { - static DWORD delay = 0; - if (!delay && !SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &delay, 0)) - delay = kDefaultMenuShowDelay; - return delay; -} - -} // namespace views diff --git a/chromium/ui/views/mouse_watcher.cc b/chromium/ui/views/mouse_watcher_aura.cc index bee59848dba..3793f0e9671 100644 --- a/chromium/ui/views/mouse_watcher.cc +++ b/chromium/ui/views/mouse_watcher_aura.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2014 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. @@ -9,16 +9,13 @@ #include "base/event_types.h" #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" -#include "ui/events/event_constants.h" -#include "ui/events/event_utils.h" -#include "ui/gfx/screen.h" - -#if defined(USE_AURA) #include "ui/aura/env.h" #include "ui/aura/window.h" #include "ui/events/event.h" +#include "ui/events/event_constants.h" #include "ui/events/event_handler.h" -#endif +#include "ui/events/event_utils.h" +#include "ui/gfx/screen.h" namespace views { @@ -26,90 +23,6 @@ namespace views { // the listener is notified. const int kNotifyListenerTimeMs = 300; -#if defined(OS_WIN) && !defined(USE_AURA) -class MouseWatcher::Observer : public base::MessageLoopForUI::Observer { - public: - explicit Observer(MouseWatcher* mouse_watcher) - : mouse_watcher_(mouse_watcher), - notify_listener_factory_(this) { - base::MessageLoopForUI::current()->AddObserver(this); - } - - virtual ~Observer() { - base::MessageLoopForUI::current()->RemoveObserver(this); - } - - // MessageLoop::Observer implementation: - virtual base::EventStatus WillProcessEvent( - const base::NativeEvent& event) OVERRIDE { - return base::EVENT_CONTINUE; - } - - virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE { - // We spy on three different Windows messages here to see if the mouse has - // moved out of the bounds of the current view. The messages are: - // - // WM_MOUSEMOVE: - // For when the mouse moves from the view into the rest of the browser UI, - // i.e. within the bounds of the same windows HWND. - // WM_MOUSELEAVE: - // For when the mouse moves out of the bounds of the view's HWND. - // WM_NCMOUSELEAVE: - // For notification when the mouse leaves the _non-client_ area. - // - switch (event.message) { - case WM_MOUSEMOVE: - HandleGlobalMouseMoveEvent(MouseWatcherHost::MOUSE_MOVE); - break; - case WM_MOUSELEAVE: - case WM_NCMOUSELEAVE: - HandleGlobalMouseMoveEvent(MouseWatcherHost::MOUSE_EXIT); - break; - } - } - - private: - MouseWatcherHost* host() const { return mouse_watcher_->host_.get(); } - - // Called from the message loop observer when a mouse movement has occurred. - void HandleGlobalMouseMoveEvent(MouseWatcherHost::MouseEventType event_type) { - bool contained = host()->Contains( - // TODO(scottmg): Native is wrong http://crbug.com/133312 - gfx::Screen::GetNativeScreen()->GetCursorScreenPoint(), - event_type); - if (!contained) { - // Mouse moved outside the host's zone, start a timer to notify the - // listener. - if (!notify_listener_factory_.HasWeakPtrs()) { - base::MessageLoop::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&Observer::NotifyListener, - notify_listener_factory_.GetWeakPtr()), - event_type == MouseWatcherHost::MOUSE_MOVE - ? base::TimeDelta::FromMilliseconds(kNotifyListenerTimeMs) - : mouse_watcher_->notify_on_exit_time_); - } - } else { - // Mouse moved quickly out of the host and then into it again, so cancel - // the timer. - notify_listener_factory_.InvalidateWeakPtrs(); - } - } - - void NotifyListener() { - mouse_watcher_->NotifyListener(); - // WARNING: we've been deleted. - } - - private: - MouseWatcher* mouse_watcher_; - - // A factory that is used to construct a delayed callback to the listener. - base::WeakPtrFactory<Observer> notify_listener_factory_; - - DISALLOW_COPY_AND_ASSIGN(Observer); -}; -#else class MouseWatcher::Observer : public ui::EventHandler { public: explicit Observer(MouseWatcher* mouse_watcher) @@ -177,7 +90,6 @@ class MouseWatcher::Observer : public ui::EventHandler { DISALLOW_COPY_AND_ASSIGN(Observer); }; -#endif MouseWatcherListener::~MouseWatcherListener() { } diff --git a/chromium/ui/views/native_cursor.h b/chromium/ui/views/native_cursor.h new file mode 100644 index 00000000000..81f6e213ae2 --- /dev/null +++ b/chromium/ui/views/native_cursor.h @@ -0,0 +1,21 @@ +// Copyright 2014 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_NATIVE_CURSOR_H_ +#define UI_VIEWS_NATIVE_CURSOR_H_ + +#include "ui/gfx/native_widget_types.h" +#include "ui/views/views_export.h" + +namespace views { + +VIEWS_EXPORT gfx::NativeCursor GetNativeIBeamCursor(); +VIEWS_EXPORT gfx::NativeCursor GetNativeHandCursor(); +VIEWS_EXPORT gfx::NativeCursor GetNativeColumnResizeCursor(); +VIEWS_EXPORT gfx::NativeCursor GetNativeEastWestResizeCursor(); +VIEWS_EXPORT gfx::NativeCursor GetNativeNorthSouthResizeCursor(); + +} // namespace views + +#endif // UI_VIEWS_NATIVE_CURSOR_H_ diff --git a/chromium/ui/views/native_cursor_aura.cc b/chromium/ui/views/native_cursor_aura.cc new file mode 100644 index 00000000000..cafe684aa0b --- /dev/null +++ b/chromium/ui/views/native_cursor_aura.cc @@ -0,0 +1,31 @@ +// Copyright 2014 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/native_cursor.h" + +#include "ui/base/cursor/cursor.h" + +namespace views { + +gfx::NativeCursor GetNativeIBeamCursor() { + return ui::kCursorIBeam; +} + +gfx::NativeCursor GetNativeHandCursor() { + return ui::kCursorHand; +} + +gfx::NativeCursor GetNativeColumnResizeCursor() { + return ui::kCursorColumnResize; +} + +gfx::NativeCursor GetNativeEastWestResizeCursor() { + return ui::kCursorEastWestResize; +} + +gfx::NativeCursor GetNativeNorthSouthResizeCursor() { + return ui::kCursorNorthSouthResize; +} + +} // namespace views diff --git a/chromium/ui/views/native_cursor_mac.mm b/chromium/ui/views/native_cursor_mac.mm new file mode 100644 index 00000000000..967484deb72 --- /dev/null +++ b/chromium/ui/views/native_cursor_mac.mm @@ -0,0 +1,39 @@ +// Copyright 2014 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/native_cursor.h" + +#include <Cocoa/Cocoa.h> + +namespace views { + +gfx::NativeCursor GetNativeIBeamCursor() { + return [NSCursor IBeamCursor]; +} + +gfx::NativeCursor GetNativeArrowCursor() { + return [NSCursor arrowCursor]; +} + +gfx::NativeCursor GetNativeHandCursor() { + return [NSCursor pointingHandCursor]; +} + +gfx::NativeCursor GetNativeColumnResizeCursor() { + return [NSCursor resizeLeftRightCursor]; +} + +gfx::NativeCursor GetNativeEastWestResizeCursor() { + NOTIMPLEMENTED(); + // TODO(tapted): This is the wrong cursor. Fetch the right one from WebCursor + // or ResourceBundle or CoreCursor private API. + return [NSCursor resizeLeftRightCursor]; +} + +gfx::NativeCursor GetNativeNorthSouthResizeCursor() { + NOTIMPLEMENTED(); + return [NSCursor resizeUpDownCursor]; +} + +} // namespace views diff --git a/chromium/ui/views/painter.cc b/chromium/ui/views/painter.cc index b6a2e7d4c6f..ed0e65f7f85 100644 --- a/chromium/ui/views/painter.cc +++ b/chromium/ui/views/painter.cc @@ -13,6 +13,7 @@ #include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_skia_operations.h" #include "ui/gfx/insets.h" +#include "ui/gfx/nine_image_painter.h" #include "ui/gfx/point.h" #include "ui/gfx/rect.h" #include "ui/views/view.h" @@ -150,7 +151,7 @@ void GradientPainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) { p[1].iset(0, size.height()); skia::RefPtr<SkShader> s = skia::AdoptRef(SkGradientShader::CreateLinear( - p, colors_.get(), pos_.get(), count_, SkShader::kClamp_TileMode, NULL)); + p, colors_.get(), pos_.get(), count_, SkShader::kClamp_TileMode)); paint.setStyle(SkPaint::kFill_Style); paint.setShader(s.get()); @@ -173,117 +174,34 @@ class VIEWS_EXPORT ImagePainter : public Painter { virtual ~ImagePainter(); - // Returns true if the images are empty. - bool IsEmpty() const; - // Painter: virtual gfx::Size GetMinimumSize() const OVERRIDE; virtual void Paint(gfx::Canvas* canvas, const gfx::Size& size) OVERRIDE; private: - // Stretches the given image over the specified canvas area. - static void Fill(gfx::Canvas* c, - const gfx::ImageSkia& i, - int x, - int y, - int w, - int h); - - // Images are numbered as depicted below. - // ____________________ - // |__i0__|__i1__|__i2__| - // |__i3__|__i4__|__i5__| - // |__i6__|__i7__|__i8__| - gfx::ImageSkia images_[9]; + scoped_ptr<gfx::NineImagePainter> nine_painter_; DISALLOW_COPY_AND_ASSIGN(ImagePainter); }; -ImagePainter::ImagePainter(const int image_ids[]) { - ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - for (size_t i = 0; i < 9; ++i) - if (image_ids[i] != 0) - images_[i] = *rb.GetImageSkiaNamed(image_ids[i]); +ImagePainter::ImagePainter(const int image_ids[]) + : nine_painter_(ui::CreateNineImagePainter(image_ids)) { } ImagePainter::ImagePainter(const gfx::ImageSkia& image, - const gfx::Insets& insets) { - DCHECK_GE(image.width(), insets.width()); - DCHECK_GE(image.height(), insets.height()); - - // Extract subsets of the original image to match the |images_| format. - const int x[] = - { 0, insets.left(), image.width() - insets.right(), image.width() }; - const int y[] = - { 0, insets.top(), image.height() - insets.bottom(), image.height() }; - - for (size_t j = 0; j < 3; ++j) { - for (size_t i = 0; i < 3; ++i) { - images_[i + j * 3] = gfx::ImageSkiaOperations::ExtractSubset(image, - gfx::Rect(x[i], y[j], x[i + 1] - x[i], y[j + 1] - y[j])); - } - } + const gfx::Insets& insets) + : nine_painter_(new gfx::NineImagePainter(image, insets)) { } ImagePainter::~ImagePainter() { } -bool ImagePainter::IsEmpty() const { - return images_[0].isNull(); -} - gfx::Size ImagePainter::GetMinimumSize() const { - return IsEmpty() ? gfx::Size() : gfx::Size( - images_[0].width() + images_[1].width() + images_[2].width(), - images_[0].height() + images_[3].height() + images_[6].height()); + return nine_painter_->GetMinimumSize(); } void ImagePainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) { - if (IsEmpty()) - return; - - // In case the corners and edges don't all have the same width/height, we draw - // the center first, and extend it out in all directions to the edges of the - // images with the smallest widths/heights. This way there will be no - // unpainted areas, though some corners or edges might overlap the center. - int w = size.width(); - int i0w = images_[0].width(); - int i2w = images_[2].width(); - int i3w = images_[3].width(); - int i5w = images_[5].width(); - int i6w = images_[6].width(); - int i8w = images_[8].width(); - int i4x = std::min(std::min(i0w, i3w), i6w); - int i4w = w - i4x - std::min(std::min(i2w, i5w), i8w); - int h = size.height(); - int i0h = images_[0].height(); - int i1h = images_[1].height(); - int i2h = images_[2].height(); - int i6h = images_[6].height(); - int i7h = images_[7].height(); - int i8h = images_[8].height(); - int i4y = std::min(std::min(i0h, i1h), i2h); - int i4h = h - i4y - std::min(std::min(i6h, i7h), i8h); - if (!images_[4].isNull()) - Fill(canvas, images_[4], i4x, i4y, i4w, i4h); - canvas->DrawImageInt(images_[0], 0, 0); - Fill(canvas, images_[1], i0w, 0, w - i0w - i2w, i1h); - canvas->DrawImageInt(images_[2], w - i2w, 0); - Fill(canvas, images_[3], 0, i0h, i3w, h - i0h - i6h); - Fill(canvas, images_[5], w - i5w, i2h, i5w, h - i2h - i8h); - canvas->DrawImageInt(images_[6], 0, h - i6h); - Fill(canvas, images_[7], i6w, h - i7h, w - i6w - i8w, i7h); - canvas->DrawImageInt(images_[8], w - i8w, h - i8h); -} - -// static -void ImagePainter::Fill(gfx::Canvas* c, - const gfx::ImageSkia& i, - int x, - int y, - int w, - int h) { - c->DrawImageInt(i, 0, 0, i.width(), i.height(), x, y, w, h, false); + nine_painter_->Paint(canvas, gfx::Rect(size)); } } // namespace diff --git a/chromium/ui/views/painter.h b/chromium/ui/views/painter.h index 3799e91e86c..6efd281f11a 100644 --- a/chromium/ui/views/painter.h +++ b/chromium/ui/views/painter.h @@ -9,6 +9,7 @@ #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" #include "third_party/skia/include/core/SkColor.h" +#include "ui/base/nine_image_painter_factory.h" #include "ui/views/views_export.h" namespace gfx { @@ -19,11 +20,6 @@ class Rect; class Size; } -// A macro to define arrays of IDR constants used with CreateImageGridPainter. -#define IMAGE_GRID(x) { x ## _TOP_LEFT, x ## _TOP, x ## _TOP_RIGHT, \ - x ## _LEFT, x ## _CENTER, x ## _RIGHT, \ - x ## _BOTTOM_LEFT, x ## _BOTTOM, x ## _BOTTOM_RIGHT, } - namespace views { class View; diff --git a/chromium/ui/views/run_all_unittests.cc b/chromium/ui/views/run_all_unittests.cc index 499d339c5f9..e5d7b9d61f0 100644 --- a/chromium/ui/views/run_all_unittests.cc +++ b/chromium/ui/views/run_all_unittests.cc @@ -8,8 +8,10 @@ #include "base/path_service.h" #include "base/test/launcher/unit_test_launcher.h" #include "base/test/test_suite.h" +#include "testing/gtest/include/gtest/gtest.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/ui_base_paths.h" +#include "ui/gl/gl_surface.h" class ViewTestSuite : public base::TestSuite { public: @@ -18,15 +20,12 @@ class ViewTestSuite : public base::TestSuite { protected: virtual void Initialize() OVERRIDE { base::TestSuite::Initialize(); + gfx::GLSurface::InitializeOneOffForTests(); ui::RegisterPathProvider(); - base::FilePath pak_dir; - PathService::Get(base::DIR_MODULE, &pak_dir); - - base::FilePath pak_file; - pak_file = pak_dir.Append(FILE_PATH_LITERAL("ui_test.pak")); - - ui::ResourceBundle::InitSharedInstanceWithPakPath(pak_file); + base::FilePath ui_test_pak_path; + ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path)); + ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path); } virtual void Shutdown() OVERRIDE { diff --git a/chromium/ui/views/touchui/touch_editing_menu.cc b/chromium/ui/views/touchui/touch_editing_menu.cc index dec91139093..b17039c9727 100644 --- a/chromium/ui/views/touchui/touch_editing_menu.cc +++ b/chromium/ui/views/touchui/touch_editing_menu.cc @@ -9,6 +9,7 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" +#include "ui/gfx/font_list.h" #include "ui/gfx/insets.h" #include "ui/gfx/text_utils.h" #include "ui/views/bubble/bubble_border.h" @@ -38,11 +39,11 @@ namespace views { TouchEditingMenuView::TouchEditingMenuView( TouchEditingMenuController* controller, - gfx::Rect anchor_rect, + const gfx::Rect& anchor_rect, + const gfx::Size& handle_image_size, gfx::NativeView context) : BubbleDelegateView(NULL, views::BubbleBorder::BOTTOM_CENTER), controller_(controller) { - SetAnchorRect(anchor_rect); set_shadow(views::BubbleBorder::SMALL_SHADOW); set_parent_window(context); set_margins(gfx::Insets(kMenuMargin, kMenuMargin, kMenuMargin, kMenuMargin)); @@ -52,6 +53,16 @@ TouchEditingMenuView::TouchEditingMenuView( SetLayoutManager(new BoxLayout(BoxLayout::kHorizontal, 0, 0, kSpacingBetweenButtons)); CreateButtons(); + + // After buttons are created, check if there is enough room between handles to + // show the menu and adjust anchor rect properly if needed, just in case the + // menu is needed to be shown under the selection. + gfx::Rect adjusted_anchor_rect(anchor_rect); + int menu_width = GetPreferredSize().width(); + if (menu_width > anchor_rect.width() - handle_image_size.width()) + adjusted_anchor_rect.Inset(0, 0, 0, -handle_image_size.height()); + SetAnchorRect(adjusted_anchor_rect); + views::BubbleDelegateView::CreateBubble(this); GetWidget()->Show(); } @@ -62,12 +73,15 @@ TouchEditingMenuView::~TouchEditingMenuView() { // static TouchEditingMenuView* TouchEditingMenuView::Create( TouchEditingMenuController* controller, - gfx::Rect anchor_rect, + const gfx::Rect& anchor_rect, + const gfx::Size& handle_image_size, gfx::NativeView context) { if (controller) { for (size_t i = 0; i < arraysize(kMenuCommands); i++) { - if (controller->IsCommandIdEnabled(kMenuCommands[i])) - return new TouchEditingMenuView(controller, anchor_rect, context); + if (controller->IsCommandIdEnabled(kMenuCommands[i])) { + return new TouchEditingMenuView(controller, anchor_rect, + handle_image_size, context); + } } } return NULL; @@ -121,25 +135,27 @@ void TouchEditingMenuView::CreateButtons() { // Finally, add ellipses button. AddChildView(CreateButton( - UTF8ToUTF16(kEllipsesButtonText), kEllipsesButtonTag)); + base::UTF8ToUTF16(kEllipsesButtonText), kEllipsesButtonTag)); Layout(); } -Button* TouchEditingMenuView::CreateButton(const string16& title, int tag) { - string16 label = gfx::RemoveAcceleratorChar(title, '&', NULL, NULL); +Button* TouchEditingMenuView::CreateButton(const base::string16& title, + int tag) { + base::string16 label = gfx::RemoveAcceleratorChar(title, '&', NULL, NULL); LabelButton* button = new LabelButton(this, label); button->SetFocusable(true); button->set_request_focus_on_press(false); - gfx::Font font = ui::ResourceBundle::GetSharedInstance().GetFont( - ui::ResourceBundle::SmallFont); + const gfx::FontList& font_list = + ui::ResourceBundle::GetSharedInstance().GetFontList( + ui::ResourceBundle::SmallFont); scoped_ptr<LabelButtonBorder> button_border( new LabelButtonBorder(button->style())); - int v_border = (kMenuButtonHeight - font.GetHeight()) / 2; - int h_border = (kMenuButtonWidth - font.GetStringWidth(label)) / 2; + int v_border = (kMenuButtonHeight - font_list.GetHeight()) / 2; + int h_border = (kMenuButtonWidth - gfx::GetStringWidth(label, font_list)) / 2; button_border->set_insets( gfx::Insets(v_border, h_border, v_border, h_border)); - button->set_border(button_border.release()); - button->SetFont(font); + button->SetBorder(button_border.PassAs<Border>()); + button->SetFontList(font_list); button->set_tag(tag); return button; } diff --git a/chromium/ui/views/touchui/touch_editing_menu.h b/chromium/ui/views/touchui/touch_editing_menu.h index 5dfeaef373a..19f0d34a664 100644 --- a/chromium/ui/views/touchui/touch_editing_menu.h +++ b/chromium/ui/views/touchui/touch_editing_menu.h @@ -45,14 +45,16 @@ class VIEWS_EXPORT TouchEditingMenuView : public BubbleDelegateView, // If there are no actions available for the menu, returns NULL. Otherwise, // returns a new instance of TouchEditingMenuView. static TouchEditingMenuView* Create(TouchEditingMenuController* controller, - gfx::Rect anchor_rect, + const gfx::Rect& anchor_rect, + const gfx::Size& handle_image_size, gfx::NativeView context); void Close(); private: TouchEditingMenuView(TouchEditingMenuController* controller, - gfx::Rect anchor_rect, + const gfx::Rect& anchor_rect, + const gfx::Size& handle_image_size, gfx::NativeView context); // views::WidgetDelegate overrides: @@ -69,7 +71,7 @@ class VIEWS_EXPORT TouchEditingMenuView : public BubbleDelegateView, void CreateButtons(); // Helper method to create a single button. - Button* CreateButton(const string16& title, int tag); + Button* CreateButton(const base::string16& title, int tag); TouchEditingMenuController* controller_; diff --git a/chromium/ui/views/touchui/touch_selection_controller_impl.cc b/chromium/ui/views/touchui/touch_selection_controller_impl.cc index 41720be7033..3e005a64ea6 100644 --- a/chromium/ui/views/touchui/touch_selection_controller_impl.cc +++ b/chromium/ui/views/touchui/touch_selection_controller_impl.cc @@ -7,16 +7,19 @@ #include "base/time/time.h" #include "grit/ui_resources.h" #include "grit/ui_strings.h" +#include "ui/aura/client/cursor_client.h" +#include "ui/aura/env.h" +#include "ui/aura/window.h" #include "ui/base/resource/resource_bundle.h" -#include "ui/base/ui_base_switches_util.h" #include "ui/gfx/canvas.h" #include "ui/gfx/image/image.h" #include "ui/gfx/path.h" #include "ui/gfx/rect.h" #include "ui/gfx/screen.h" #include "ui/gfx/size.h" -#include "ui/views/corewm/shadow_types.h" #include "ui/views/widget/widget.h" +#include "ui/wm/core/masked_window_targeter.h" +#include "ui/wm/core/window_animations.h" namespace { @@ -56,21 +59,27 @@ const int kSelectionHandleVertPadding = 20; const int kContextMenuTimoutMs = 200; +const int kSelectionHandleQuickFadeDurationMs = 50; + +// Minimum height for selection handle bar. If the bar height is going to be +// less than this value, handle will not be shown. +const int kSelectionHandleBarMinHeight = 5; +// Maximum amount that selection handle bar can stick out of client view's +// boundaries. +const int kSelectionHandleBarBottomAllowance = 3; + // Creates a widget to host SelectionHandleView. views::Widget* CreateTouchSelectionPopupWidget( gfx::NativeView context, views::WidgetDelegate* widget_delegate) { views::Widget* widget = new views::Widget; - views::Widget::InitParams params(views::Widget::InitParams::TYPE_TOOLTIP); - params.can_activate = false; + views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; + params.shadow_type = views::Widget::InitParams::SHADOW_TYPE_NONE; params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - params.context = context; + params.parent = context; params.delegate = widget_delegate; widget->Init(params); -#if defined(USE_AURA) - SetShadowType(widget->GetNativeView(), views::corewm::SHADOW_TYPE_NONE); -#endif return widget; } @@ -97,8 +106,8 @@ gfx::Rect Union(const gfx::Rect& r1, const gfx::Rect& r2) { return gfx::Rect(rx, ry, rr - rx, rb - ry); } -// Convenience method to convert a |rect| from screen to the |client|'s -// coordinate system. +// Convenience methods to convert a |rect| from screen to the |client|'s +// coordinate system and vice versa. // Note that this is not quite correct because it does not take into account // transforms such as rotation and scaling. This should be in TouchEditable. // TODO(varunjain): Fix this. @@ -107,29 +116,57 @@ gfx::Rect ConvertFromScreen(ui::TouchEditable* client, const gfx::Rect& rect) { client->ConvertPointFromScreen(&origin); return gfx::Rect(origin, rect.size()); } +gfx::Rect ConvertToScreen(ui::TouchEditable* client, const gfx::Rect& rect) { + gfx::Point origin = rect.origin(); + client->ConvertPointToScreen(&origin); + return gfx::Rect(origin, rect.size()); +} } // namespace namespace views { +typedef TouchSelectionControllerImpl::EditingHandleView EditingHandleView; + +class TouchHandleWindowTargeter : public wm::MaskedWindowTargeter { + public: + TouchHandleWindowTargeter(aura::Window* window, + EditingHandleView* handle_view); + + virtual ~TouchHandleWindowTargeter() {} + + private: + // wm::MaskedWindowTargeter: + virtual bool GetHitTestMask(aura::Window* window, + gfx::Path* mask) const OVERRIDE; + + EditingHandleView* handle_view_; + + DISALLOW_COPY_AND_ASSIGN(TouchHandleWindowTargeter); +}; + // A View that displays the text selection handle. class TouchSelectionControllerImpl::EditingHandleView : public views::WidgetDelegateView { public: - explicit EditingHandleView(TouchSelectionControllerImpl* controller, - gfx::NativeView context) + EditingHandleView(TouchSelectionControllerImpl* controller, + gfx::NativeView context) : controller_(controller), drag_offset_(0), draw_invisible_(false) { widget_.reset(CreateTouchSelectionPopupWidget(context, this)); widget_->SetContentsView(this); - widget_->SetAlwaysOnTop(true); + + aura::Window* window = widget_->GetNativeWindow(); + window->SetEventTargeter(scoped_ptr<ui::EventTargeter>( + new TouchHandleWindowTargeter(window, this))); // We are owned by the TouchSelectionController. set_owned_by_client(); } virtual ~EditingHandleView() { + SetWidgetVisible(false, false); } // Overridden from views::WidgetDelegateView: @@ -193,7 +230,7 @@ class TouchSelectionControllerImpl::EditingHandleView } } - virtual gfx::Size GetPreferredSize() OVERRIDE { + virtual gfx::Size GetPreferredSize() const OVERRIDE { gfx::Size image_size = GetHandleImageSize(); return gfx::Size(image_size.width() + 2 * kSelectionHandleHorizPadding, image_size.height() + selection_rect_.height() + @@ -204,9 +241,13 @@ class TouchSelectionControllerImpl::EditingHandleView return widget_->IsVisible(); } - void SetWidgetVisible(bool visible) { + void SetWidgetVisible(bool visible, bool quick) { if (widget_->IsVisible() == visible) return; + wm::SetWindowVisibilityAnimationDuration( + widget_->GetNativeView(), + base::TimeDelta::FromMilliseconds( + quick ? kSelectionHandleQuickFadeDurationMs : 0)); if (visible) widget_->Show(); else @@ -235,6 +276,8 @@ class TouchSelectionControllerImpl::EditingHandleView SchedulePaint(); } + const gfx::Rect& selection_rect() const { return selection_rect_; } + private: scoped_ptr<Widget> widget_; TouchSelectionControllerImpl* controller_; @@ -254,6 +297,24 @@ class TouchSelectionControllerImpl::EditingHandleView DISALLOW_COPY_AND_ASSIGN(EditingHandleView); }; +TouchHandleWindowTargeter::TouchHandleWindowTargeter( + aura::Window* window, + EditingHandleView* handle_view) + : wm::MaskedWindowTargeter(window), + handle_view_(handle_view) { +} + +bool TouchHandleWindowTargeter::GetHitTestMask(aura::Window* window, + gfx::Path* mask) const { + const gfx::Rect& selection_rect = handle_view_->selection_rect(); + gfx::Size image_size = GetHandleImageSize(); + mask->addRect(SkIntToScalar(0), SkIntToScalar(selection_rect.height()), + SkIntToScalar(image_size.width()) + 2 * kSelectionHandleHorizPadding, + SkIntToScalar(selection_rect.height() + image_size.height() + + kSelectionHandleVertPadding)); + return true; +} + TouchSelectionControllerImpl::TouchSelectionControllerImpl( ui::TouchEditable* client_view) : client_view_(client_view), @@ -270,10 +331,12 @@ TouchSelectionControllerImpl::TouchSelectionControllerImpl( client_view_->GetNativeView()); if (client_widget_) client_widget_->AddObserver(this); + aura::Env::GetInstance()->AddPreTargetHandler(this); } TouchSelectionControllerImpl::~TouchSelectionControllerImpl() { HideContextMenu(); + aura::Env::GetInstance()->RemovePreTargetHandler(this); if (client_widget_) client_widget_->RemoveObserver(this); } @@ -281,21 +344,26 @@ TouchSelectionControllerImpl::~TouchSelectionControllerImpl() { void TouchSelectionControllerImpl::SelectionChanged() { gfx::Rect r1, r2; client_view_->GetSelectionEndPoints(&r1, &r2); - gfx::Point screen_pos_1(r1.origin()); - client_view_->ConvertPointToScreen(&screen_pos_1); - gfx::Point screen_pos_2(r2.origin()); - client_view_->ConvertPointToScreen(&screen_pos_2); - gfx::Rect screen_rect_1(screen_pos_1, r1.size()); - gfx::Rect screen_rect_2(screen_pos_2, r2.size()); - if (screen_rect_1 == selection_end_point_1_ && - screen_rect_2 == selection_end_point_2_) + gfx::Rect screen_rect_1 = ConvertToScreen(client_view_, r1); + gfx::Rect screen_rect_2 = ConvertToScreen(client_view_, r2); + gfx::Rect client_bounds = client_view_->GetBounds(); + if (r1.y() < client_bounds.y()) + r1.Inset(0, client_bounds.y() - r1.y(), 0, 0); + if (r2.y() < client_bounds.y()) + r2.Inset(0, client_bounds.y() - r2.y(), 0, 0); + gfx::Rect screen_rect_1_clipped = ConvertToScreen(client_view_, r1); + gfx::Rect screen_rect_2_clipped = ConvertToScreen(client_view_, r2); + if (screen_rect_1_clipped == selection_end_point_1_clipped_ && + screen_rect_2_clipped == selection_end_point_2_clipped_) return; selection_end_point_1_ = screen_rect_1; selection_end_point_2_ = screen_rect_2; + selection_end_point_1_clipped_ = screen_rect_1_clipped; + selection_end_point_2_clipped_ = screen_rect_2_clipped; if (client_view_->DrawsHandles()) { - UpdateContextMenu(r1.origin(), r2.origin()); + UpdateContextMenu(); return; } if (dragging_handle_) { @@ -306,14 +374,14 @@ void TouchSelectionControllerImpl::SelectionChanged() { // If the new location of this handle is out of client view, its widget // should not get hidden, since it should still receive touch events. // Hence, we are not using |SetHandleSelectionRect()| method here. - dragging_handle_->SetSelectionRectInScreen(screen_rect_2); + dragging_handle_->SetSelectionRectInScreen(screen_rect_2_clipped); // Temporary fix for selection handle going outside a window. On a webpage, // the page should scroll if the selection handle is dragged outside the // window. That does not happen currently. So we just hide the handle for // now. // TODO(varunjain): Fix this: crbug.com/269003 - dragging_handle_->SetDrawInvisible(!client_view_->GetBounds().Contains(r2)); + dragging_handle_->SetDrawInvisible(!ShouldShowHandleFor(r2)); if (dragging_handle_ != cursor_handle_.get()) { // The non-dragging-handle might have recently become visible. @@ -324,23 +392,27 @@ void TouchSelectionControllerImpl::SelectionChanged() { // selection and the other handle to the start of selection. selection_end_point_1_ = screen_rect_2; selection_end_point_2_ = screen_rect_1; + selection_end_point_1_clipped_ = screen_rect_2_clipped; + selection_end_point_2_clipped_ = screen_rect_1_clipped; } - SetHandleSelectionRect(non_dragging_handle, r1, screen_rect_1); + SetHandleSelectionRect(non_dragging_handle, r1, screen_rect_1_clipped); } } else { - UpdateContextMenu(r1.origin(), r2.origin()); + UpdateContextMenu(); // Check if there is any selection at all. - if (screen_pos_1 == screen_pos_2) { - selection_handle_1_->SetWidgetVisible(false); - selection_handle_2_->SetWidgetVisible(false); - SetHandleSelectionRect(cursor_handle_.get(), r1, screen_rect_1); + if (screen_rect_1.origin() == screen_rect_2.origin()) { + selection_handle_1_->SetWidgetVisible(false, false); + selection_handle_2_->SetWidgetVisible(false, false); + SetHandleSelectionRect(cursor_handle_.get(), r1, screen_rect_1_clipped); return; } - cursor_handle_->SetWidgetVisible(false); - SetHandleSelectionRect(selection_handle_1_.get(), r1, screen_rect_1); - SetHandleSelectionRect(selection_handle_2_.get(), r2, screen_rect_2); + cursor_handle_->SetWidgetVisible(false, false); + SetHandleSelectionRect(selection_handle_1_.get(), r1, + screen_rect_1_clipped); + SetHandleSelectionRect(selection_handle_2_.get(), r2, + screen_rect_2_clipped); } } @@ -348,6 +420,12 @@ bool TouchSelectionControllerImpl::IsHandleDragInProgress() { return !!dragging_handle_; } +void TouchSelectionControllerImpl::HideHandles(bool quick) { + selection_handle_1_->SetWidgetVisible(false, quick); + selection_handle_2_->SetWidgetVisible(false, quick); + cursor_handle_->SetWidgetVisible(false, quick); +} + void TouchSelectionControllerImpl::SetDraggingHandle( EditingHandleView* handle) { dragging_handle_ = handle; @@ -397,11 +475,20 @@ void TouchSelectionControllerImpl::SetHandleSelectionRect( EditingHandleView* handle, const gfx::Rect& rect, const gfx::Rect& rect_in_screen) { - handle->SetWidgetVisible(client_view_->GetBounds().Contains(rect)); + handle->SetWidgetVisible(ShouldShowHandleFor(rect), false); if (handle->IsWidgetVisible()) handle->SetSelectionRectInScreen(rect_in_screen); } +bool TouchSelectionControllerImpl::ShouldShowHandleFor( + const gfx::Rect& rect) const { + if (rect.height() < kSelectionHandleBarMinHeight) + return false; + gfx::Rect bounds = client_view_->GetBounds(); + bounds.Inset(0, 0, 0, -kSelectionHandleBarBottomAllowance); + return bounds.Contains(rect); +} + bool TouchSelectionControllerImpl::IsCommandIdEnabled(int command_id) const { return client_view_->IsCommandIdEnabled(command_id); } @@ -438,16 +525,31 @@ void TouchSelectionControllerImpl::OnWidgetBoundsChanged( SelectionChanged(); } +void TouchSelectionControllerImpl::OnKeyEvent(ui::KeyEvent* event) { + client_view_->DestroyTouchSelection(); +} + +void TouchSelectionControllerImpl::OnMouseEvent(ui::MouseEvent* event) { + aura::client::CursorClient* cursor_client = aura::client::GetCursorClient( + client_view_->GetNativeView()->GetRootWindow()); + if (!cursor_client || cursor_client->IsMouseEventsEnabled()) + client_view_->DestroyTouchSelection(); +} + +void TouchSelectionControllerImpl::OnScrollEvent(ui::ScrollEvent* event) { + client_view_->DestroyTouchSelection(); +} + void TouchSelectionControllerImpl::ContextMenuTimerFired() { // Get selection end points in client_view's space. gfx::Rect end_rect_1_in_screen; gfx::Rect end_rect_2_in_screen; if (cursor_handle_->IsWidgetVisible()) { - end_rect_1_in_screen = selection_end_point_1_; + end_rect_1_in_screen = selection_end_point_1_clipped_; end_rect_2_in_screen = end_rect_1_in_screen; } else { - end_rect_1_in_screen = selection_end_point_1_; - end_rect_2_in_screen = selection_end_point_2_; + end_rect_1_in_screen = selection_end_point_1_clipped_; + end_rect_2_in_screen = selection_end_point_2_clipped_; } // Convert from screen to client. @@ -458,19 +560,19 @@ void TouchSelectionControllerImpl::ContextMenuTimerFired() { // in the middle of the end points on the top. Else, we show it above the // visible handle. If no handle is visible, we do not show the menu. gfx::Rect menu_anchor; - gfx::Rect client_bounds = client_view_->GetBounds(); - if (client_bounds.Contains(end_rect_1) && - client_bounds.Contains(end_rect_2)) + if (ShouldShowHandleFor(end_rect_1) && + ShouldShowHandleFor(end_rect_2)) menu_anchor = Union(end_rect_1_in_screen,end_rect_2_in_screen); - else if (client_bounds.Contains(end_rect_1)) + else if (ShouldShowHandleFor(end_rect_1)) menu_anchor = end_rect_1_in_screen; - else if (client_bounds.Contains(end_rect_2)) + else if (ShouldShowHandleFor(end_rect_2)) menu_anchor = end_rect_2_in_screen; else return; DCHECK(!context_menu_); context_menu_ = TouchEditingMenuView::Create(this, menu_anchor, + GetHandleImageSize(), client_view_->GetNativeView()); } @@ -484,8 +586,7 @@ void TouchSelectionControllerImpl::StartContextMenuTimer() { &TouchSelectionControllerImpl::ContextMenuTimerFired); } -void TouchSelectionControllerImpl::UpdateContextMenu(const gfx::Point& p1, - const gfx::Point& p2) { +void TouchSelectionControllerImpl::UpdateContextMenu() { // Hide context menu to be shown when the timer fires. HideContextMenu(); StartContextMenuTimer(); @@ -498,6 +599,10 @@ void TouchSelectionControllerImpl::HideContextMenu() { context_menu_timer_.Stop(); } +gfx::NativeView TouchSelectionControllerImpl::GetCursorHandleNativeView() { + return cursor_handle_->GetWidget()->GetNativeView(); +} + gfx::Point TouchSelectionControllerImpl::GetSelectionHandle1Position() { return selection_handle_1_->GetScreenPosition(); } @@ -522,14 +627,4 @@ bool TouchSelectionControllerImpl::IsCursorHandleVisible() { return cursor_handle_->IsWidgetVisible(); } -ViewsTouchSelectionControllerFactory::ViewsTouchSelectionControllerFactory() { -} - -ui::TouchSelectionController* ViewsTouchSelectionControllerFactory::create( - ui::TouchEditable* client_view) { - if (switches::IsTouchEditingEnabled()) - return new views::TouchSelectionControllerImpl(client_view); - return NULL; -} - } // namespace views diff --git a/chromium/ui/views/touchui/touch_selection_controller_impl.h b/chromium/ui/views/touchui/touch_selection_controller_impl.h index dc9babe9d34..04076af2e8f 100644 --- a/chromium/ui/views/touchui/touch_selection_controller_impl.h +++ b/chromium/ui/views/touchui/touch_selection_controller_impl.h @@ -14,13 +14,20 @@ namespace views { +namespace test { +class WidgetTestInteractive; +} + // Touch specific implementation of TouchSelectionController. Responsible for // displaying selection handles and menu elements relevant in a touch interface. class VIEWS_EXPORT TouchSelectionControllerImpl : public ui::TouchSelectionController, public TouchEditingMenuController, - public WidgetObserver { + public WidgetObserver, + public ui::EventHandler { public: + class EditingHandleView; + // Use TextSelectionController::create(). explicit TouchSelectionControllerImpl( ui::TouchEditable* client_view); @@ -30,10 +37,11 @@ class VIEWS_EXPORT TouchSelectionControllerImpl // TextSelectionController. virtual void SelectionChanged() OVERRIDE; virtual bool IsHandleDragInProgress() OVERRIDE; + virtual void HideHandles(bool quick) OVERRIDE; private: friend class TouchSelectionControllerImplTest; - class EditingHandleView; + friend class test::WidgetTestInteractive; void SetDraggingHandle(EditingHandleView* handle); @@ -50,6 +58,10 @@ class VIEWS_EXPORT TouchSelectionControllerImpl void SetHandleSelectionRect(EditingHandleView* handle, const gfx::Rect& rect, const gfx::Rect& rect_in_screen); + // Checks if handle should be shown for a selection end-point at |rect|. + // |rect| should be the clipped version of the selection end-point. + bool ShouldShowHandleFor(const gfx::Rect& rect) const; + // Overridden from TouchEditingMenuController. virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE; virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE; @@ -63,18 +75,24 @@ class VIEWS_EXPORT TouchSelectionControllerImpl virtual void OnWidgetBoundsChanged(Widget* widget, const gfx::Rect& new_bounds) OVERRIDE; + // Overriden from ui::EventHandler. + virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE; + virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE; + virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE; + // Time to show context menu. void ContextMenuTimerFired(); void StartContextMenuTimer(); // Convenience method to update the position/visibility of the context menu. - void UpdateContextMenu(const gfx::Point& p1, const gfx::Point& p2); + void UpdateContextMenu(); // Convenience method for hiding context menu. void HideContextMenu(); // Convenience methods for testing. + gfx::NativeView GetCursorHandleNativeView(); gfx::Point GetSelectionHandle1Position(); gfx::Point GetSelectionHandle2Position(); gfx::Point GetCursorHandlePosition(); @@ -104,20 +122,13 @@ class VIEWS_EXPORT TouchSelectionControllerImpl // position of handles which might be invalid when handles are hidden. gfx::Rect selection_end_point_1_; gfx::Rect selection_end_point_2_; + // Selection end points, clipped to client view's boundaries. + gfx::Rect selection_end_point_1_clipped_; + gfx::Rect selection_end_point_2_clipped_; DISALLOW_COPY_AND_ASSIGN(TouchSelectionControllerImpl); }; -class VIEWS_EXPORT ViewsTouchSelectionControllerFactory - : public ui::TouchSelectionControllerFactory { - public: - ViewsTouchSelectionControllerFactory(); - - // Overridden from ui::TouchSelectionControllerFactory. - virtual ui::TouchSelectionController* create( - ui::TouchEditable* client_view) OVERRIDE; -}; - } // namespace views #endif // UI_UI_VIEWS_TOUCHUI_TOUCH_SELECTION_CONTROLLER_IMPL_H_ diff --git a/chromium/ui/views/touchui/touch_selection_controller_impl_unittest.cc b/chromium/ui/views/touchui/touch_selection_controller_impl_unittest.cc index 54c997223c2..8f21e3729bc 100644 --- a/chromium/ui/views/touchui/touch_selection_controller_impl_unittest.cc +++ b/chromium/ui/views/touchui/touch_selection_controller_impl_unittest.cc @@ -5,27 +5,44 @@ #include "base/command_line.h" #include "base/strings/utf_string_conversions.h" #include "grit/ui_resources.h" +#include "ui/aura/client/screen_position_client.h" +#include "ui/aura/test/event_generator.h" +#include "ui/aura/window.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/touch/touch_editing_controller.h" #include "ui/base/ui_base_switches.h" +#include "ui/gfx/canvas.h" #include "ui/gfx/point.h" #include "ui/gfx/rect.h" #include "ui/gfx/render_text.h" -#include "ui/views/controls/textfield/native_textfield_views.h" #include "ui/views/controls/textfield/textfield.h" +#include "ui/views/controls/textfield/textfield_test_api.h" #include "ui/views/test/views_test_base.h" #include "ui/views/touchui/touch_selection_controller_impl.h" +#include "ui/views/views_touch_selection_controller_factory.h" #include "ui/views/widget/widget.h" -#if defined(USE_AURA) -#include "ui/aura/test/event_generator.h" -#include "ui/aura/window.h" -#endif +using base::ASCIIToUTF16; +using base::UTF16ToUTF8; +using base::WideToUTF16; namespace { // Should match kSelectionHandlePadding in touch_selection_controller. const int kPadding = 10; +// Should match kSelectionHandleBarMinHeight in touch_selection_controller. +const int kBarMinHeight = 5; + +// Should match kSelectionHandleBarBottomAllowance in +// touch_selection_controller. +const int kBarBottomAllowance = 3; + +// Should match kMenuButtonWidth in touch_editing_menu. +const int kMenuButtonWidth = 63; + +// Should match size of kMenuCommands array in touch_editing_menu. +const int kMenuCommandCount = 3; + gfx::Image* GetHandleImage() { static gfx::Image* handle_image = NULL; if (!handle_image) { @@ -45,9 +62,9 @@ namespace views { class TouchSelectionControllerImplTest : public ViewsTestBase { public: TouchSelectionControllerImplTest() - : widget_(NULL), + : textfield_widget_(NULL), + widget_(NULL), textfield_(NULL), - textfield_view_(NULL), views_tsc_factory_(new ViewsTouchSelectionControllerFactory) { CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableTouchEditing); @@ -59,42 +76,68 @@ class TouchSelectionControllerImplTest : public ViewsTestBase { } virtual void TearDown() { - if (widget_) + if (textfield_widget_ && !textfield_widget_->IsClosed()) + textfield_widget_->Close(); + if (widget_ && !widget_->IsClosed()) widget_->Close(); ViewsTestBase::TearDown(); } void CreateTextfield() { textfield_ = new Textfield(); - widget_ = new Widget; + textfield_widget_ = new Widget; Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); params.bounds = gfx::Rect(0, 0, 200, 200); - widget_->Init(params); + textfield_widget_->Init(params); View* container = new View(); - widget_->SetContentsView(container); + textfield_widget_->SetContentsView(container); container->AddChildView(textfield_); - textfield_view_ = static_cast<NativeTextfieldViews*>( - textfield_->GetNativeWrapperForTesting()); - textfield_->SetBoundsRect(params.bounds); - textfield_view_->SetBoundsRect(params.bounds); + textfield_->SetBoundsRect(gfx::Rect(0, 0, 200, 20)); textfield_->set_id(1); - widget_->Show(); + textfield_widget_->Show(); - DCHECK(textfield_view_); textfield_->RequestFocus(); + + textfield_test_api_.reset(new TextfieldTestApi(textfield_)); + } + + void CreateWidget() { + widget_ = new Widget; + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.bounds = gfx::Rect(0, 0, 200, 200); + widget_->Init(params); + widget_->Show(); } protected: + static bool IsCursorHandleVisibleFor( + ui::TouchSelectionController* controller) { + TouchSelectionControllerImpl* impl = + static_cast<TouchSelectionControllerImpl*>(controller); + return impl->IsCursorHandleVisible(); + } + + gfx::Rect GetCursorRect(const gfx::SelectionModel& sel) { + return textfield_test_api_->GetRenderText()->GetCursorBounds(sel, true); + } + gfx::Point GetCursorPosition(const gfx::SelectionModel& sel) { - gfx::RenderText* render_text = textfield_view_->GetRenderText(); - gfx::Rect cursor_bounds = render_text->GetCursorBounds(sel, true); + gfx::Rect cursor_bounds = GetCursorRect(sel); return gfx::Point(cursor_bounds.x(), cursor_bounds.y()); } TouchSelectionControllerImpl* GetSelectionController() { return static_cast<TouchSelectionControllerImpl*>( - textfield_view_->touch_selection_controller_.get()); + textfield_test_api_->touch_selection_controller()); + } + + void StartTouchEditing() { + textfield_test_api_->CreateTouchSelectionControllerAndNotifyIt(); + } + + void EndTouchEditing() { + textfield_test_api_->ResetTouchSelectionController(); } void SimulateSelectionHandleDrag(gfx::Point p, int selection_handle) { @@ -114,6 +157,10 @@ class TouchSelectionControllerImplTest : public ViewsTestBase { controller->dragging_handle_ = NULL; } + gfx::NativeView GetCursorHandleNativeView() { + return GetSelectionController()->GetCursorHandleNativeView(); + } + gfx::Point GetSelectionHandle1Position() { return GetSelectionController()->GetSelectionHandle1Position(); } @@ -139,13 +186,23 @@ class TouchSelectionControllerImplTest : public ViewsTestBase { } gfx::RenderText* GetRenderText() { - return textfield_view_->GetRenderText(); + return textfield_test_api_->GetRenderText(); } + gfx::Point GetCursorHandleDragPoint() { + gfx::Point point = GetCursorHandlePosition(); + const gfx::SelectionModel& sel = textfield_->GetSelectionModel(); + int cursor_height = GetCursorRect(sel).height(); + point.Offset(GetHandleImageSize().width() / 2 + kPadding, + GetHandleImageSize().height() / 2 + cursor_height); + return point; + } + + Widget* textfield_widget_; Widget* widget_; Textfield* textfield_; - NativeTextfieldViews* textfield_view_; + scoped_ptr<TextfieldTestApi> textfield_test_api_; scoped_ptr<ViewsTouchSelectionControllerFactory> views_tsc_factory_; private: @@ -158,7 +215,7 @@ class TouchSelectionControllerImplTest : public ViewsTestBase { // handle 1's position is matched against the start of selection or the end. #define VERIFY_HANDLE_POSITIONS(cursor_at_selection_handle_1) \ { \ - gfx::SelectionModel sel = textfield_view_->GetSelectionModel(); \ + gfx::SelectionModel sel = textfield_->GetSelectionModel(); \ if (textfield_->HasSelection()) { \ EXPECT_TRUE(IsSelectionHandle1Visible()); \ EXPECT_TRUE(IsSelectionHandle2Visible()); \ @@ -197,7 +254,7 @@ TEST_F(TouchSelectionControllerImplTest, SelectionInTextfieldTest) { // Tap the textfield to invoke touch selection. ui::GestureEvent tap(ui::ET_GESTURE_TAP, 0, 0, 0, base::TimeDelta(), ui::GestureEventDetails(ui::ET_GESTURE_TAP, 1.0f, 0.0f), 0); - textfield_view_->OnGestureEvent(&tap); + textfield_->OnGestureEvent(&tap); // Test selecting a range. textfield_->SelectRange(gfx::Range(3, 7)); @@ -212,13 +269,13 @@ TEST_F(TouchSelectionControllerImplTest, SelectionInTextfieldTest) { VERIFY_HANDLE_POSITIONS(false); // Test with lost focus. - widget_->GetFocusManager()->ClearFocus(); + textfield_widget_->GetFocusManager()->ClearFocus(); EXPECT_FALSE(GetSelectionController()); // Test with focus re-gained. - widget_->GetFocusManager()->SetFocusedView(textfield_); + textfield_widget_->GetFocusManager()->SetFocusedView(textfield_); EXPECT_FALSE(GetSelectionController()); - textfield_view_->OnGestureEvent(&tap); + textfield_->OnGestureEvent(&tap); VERIFY_HANDLE_POSITIONS(false); } @@ -229,7 +286,7 @@ TEST_F(TouchSelectionControllerImplTest, SelectionInBidiTextfieldTest) { // Tap the textfield to invoke touch selection. ui::GestureEvent tap(ui::ET_GESTURE_TAP, 0, 0, 0, base::TimeDelta(), ui::GestureEventDetails(ui::ET_GESTURE_TAP, 1.0f, 0.0f), 0); - textfield_view_->OnGestureEvent(&tap); + textfield_->OnGestureEvent(&tap); // Test cursor at run boundary and with empty selection. textfield_->SelectSelectionModel( @@ -277,15 +334,15 @@ TEST_F(TouchSelectionControllerImplTest, SelectRectCallbackTest) { // Tap the textfield to invoke touch selection. ui::GestureEvent tap(ui::ET_GESTURE_TAP, 0, 0, 0, base::TimeDelta(), ui::GestureEventDetails(ui::ET_GESTURE_TAP, 1.0f, 0.0f), 0); - textfield_view_->OnGestureEvent(&tap); + textfield_->OnGestureEvent(&tap); textfield_->SelectRange(gfx::Range(3, 7)); EXPECT_EQ(UTF16ToUTF8(textfield_->GetSelectedText()), "tfie"); VERIFY_HANDLE_POSITIONS(false); // Drag selection handle 2 to right by 3 chars. - const gfx::Font& font = textfield_->GetPrimaryFont(); - int x = font.GetStringWidth(ASCIIToUTF16("ld ")); + const gfx::FontList& font_list = textfield_->GetFontList(); + int x = gfx::Canvas::GetStringWidth(ASCIIToUTF16("ld "), font_list); SimulateSelectionHandleDrag(gfx::Point(x, 0), 2); EXPECT_EQ(UTF16ToUTF8(textfield_->GetSelectedText()), "tfield "); VERIFY_HANDLE_POSITIONS(false); @@ -297,13 +354,13 @@ TEST_F(TouchSelectionControllerImplTest, SelectRectCallbackTest) { VERIFY_HANDLE_POSITIONS(true); // Drag selection handle 1 across selection handle 2. - x = font.GetStringWidth(ASCIIToUTF16("textfield with ")); + x = gfx::Canvas::GetStringWidth(ASCIIToUTF16("textfield with "), font_list); SimulateSelectionHandleDrag(gfx::Point(x, 0), 1); EXPECT_EQ(UTF16ToUTF8(textfield_->GetSelectedText()), "with "); VERIFY_HANDLE_POSITIONS(true); // Drag selection handle 2 across selection handle 1. - x = font.GetStringWidth(ASCIIToUTF16("with selected ")); + x = gfx::Canvas::GetStringWidth(ASCIIToUTF16("with selected "), font_list); SimulateSelectionHandleDrag(gfx::Point(x, 0), 2); EXPECT_EQ(UTF16ToUTF8(textfield_->GetSelectedText()), "selected "); VERIFY_HANDLE_POSITIONS(false); @@ -315,7 +372,7 @@ TEST_F(TouchSelectionControllerImplTest, SelectRectInBidiCallbackTest) { // Tap the textfield to invoke touch selection. ui::GestureEvent tap(ui::ET_GESTURE_TAP, 0, 0, 0, base::TimeDelta(), ui::GestureEventDetails(ui::ET_GESTURE_TAP, 1.0f, 0.0f), 0); - textfield_view_->OnGestureEvent(&tap); + textfield_->OnGestureEvent(&tap); // Select [c] from left to right. textfield_->SelectRange(gfx::Range(2, 3)); @@ -323,14 +380,14 @@ TEST_F(TouchSelectionControllerImplTest, SelectRectInBidiCallbackTest) { VERIFY_HANDLE_POSITIONS(false); // Drag selection handle 2 to right by 1 char. - const gfx::Font& font = textfield_->GetPrimaryFont(); - int x = font.GetStringWidth(WideToUTF16(L"\x05e3")); + const gfx::FontList& font_list = textfield_->GetFontList(); + int x = gfx::Canvas::GetStringWidth(WideToUTF16(L"\x05e3"), font_list); SimulateSelectionHandleDrag(gfx::Point(x, 0), 2); EXPECT_EQ(WideToUTF16(L"c\x05e1\x05e2"), textfield_->GetSelectedText()); VERIFY_HANDLE_POSITIONS(false); // Drag selection handle 1 to left by 1 char. - x = font.GetStringWidth(WideToUTF16(L"b")); + x = gfx::Canvas::GetStringWidth(WideToUTF16(L"b"), font_list); SimulateSelectionHandleDrag(gfx::Point(-x, 0), 1); EXPECT_EQ(WideToUTF16(L"bc\x05e1\x05e2"), textfield_->GetSelectedText()); VERIFY_HANDLE_POSITIONS(true); @@ -341,13 +398,13 @@ TEST_F(TouchSelectionControllerImplTest, SelectRectInBidiCallbackTest) { VERIFY_HANDLE_POSITIONS(false); // Drag selection handle 1 to right by 1 char. - x = font.GetStringWidth(WideToUTF16(L"\x05e3")); + x = gfx::Canvas::GetStringWidth(WideToUTF16(L"\x05e3"), font_list); SimulateSelectionHandleDrag(gfx::Point(x, 0), 1); EXPECT_EQ(WideToUTF16(L"c\x05e1\x05e2"), textfield_->GetSelectedText()); VERIFY_HANDLE_POSITIONS(true); // Drag selection handle 2 to left by 1 char. - x = font.GetStringWidth(WideToUTF16(L"b")); + x = gfx::Canvas::GetStringWidth(WideToUTF16(L"b"), font_list); SimulateSelectionHandleDrag(gfx::Point(-x, 0), 2); EXPECT_EQ(WideToUTF16(L"bc\x05e1\x05e2"), textfield_->GetSelectedText()); VERIFY_HANDLE_POSITIONS(false); @@ -366,14 +423,14 @@ TEST_F(TouchSelectionControllerImplTest, SelectRectInBidiCallbackTest) { Need further investigation on whether this is a bug in Pango and how to work around it. // Drag selection handle 2 to left by 1 char. - x = font.GetStringWidth(WideToUTF16(L"\x05e2")); + x = gfx::Canvas::GetStringWidth(WideToUTF16(L"\x05e2"), font_list); SimulateSelectionHandleDrag(gfx::Point(-x, 0), 2); EXPECT_EQ(WideToUTF16(L"\x05e1\x05e2"), textfield_->GetSelectedText()); VERIFY_HANDLE_POSITIONS(false); */ // Drag selection handle 1 to right by 1 char. - x = font.GetStringWidth(WideToUTF16(L"d")); + x = gfx::Canvas::GetStringWidth(WideToUTF16(L"d"), font_list); SimulateSelectionHandleDrag(gfx::Point(x, 0), 1); EXPECT_EQ(WideToUTF16(L"\x05e2\x05e3" L"d"), textfield_->GetSelectedText()); VERIFY_HANDLE_POSITIONS(true); @@ -385,14 +442,14 @@ TEST_F(TouchSelectionControllerImplTest, SelectRectInBidiCallbackTest) { /* TODO(xji): see detail of above commented out test case. // Drag selection handle 1 to left by 1 char. - x = font.GetStringWidth(WideToUTF16(L"\x05e2")); + x = gfx::Canvas::GetStringWidth(WideToUTF16(L"\x05e2"), font_list); SimulateSelectionHandleDrag(gfx::Point(-x, 0), 1); EXPECT_EQ(WideToUTF16(L"\x05e1\x05e2"), textfield_->GetSelectedText()); VERIFY_HANDLE_POSITIONS(true); */ // Drag selection handle 2 to right by 1 char. - x = font.GetStringWidth(WideToUTF16(L"d")); + x = gfx::Canvas::GetStringWidth(WideToUTF16(L"d"), font_list); SimulateSelectionHandleDrag(gfx::Point(x, 0), 2); EXPECT_EQ(WideToUTF16(L"\x05e2\x05e3" L"d"), textfield_->GetSelectedText()); VERIFY_HANDLE_POSITIONS(false); @@ -403,13 +460,13 @@ TEST_F(TouchSelectionControllerImplTest, SelectRectInBidiCallbackTest) { VERIFY_HANDLE_POSITIONS(false); // Drag selection handle 2 to left by 1 char. - x = font.GetStringWidth(WideToUTF16(L"c")); + x = gfx::Canvas::GetStringWidth(WideToUTF16(L"c"), font_list); SimulateSelectionHandleDrag(gfx::Point(-x, 0), 2); EXPECT_EQ(WideToUTF16(L"c\x05e1\x05e2"), textfield_->GetSelectedText()); VERIFY_HANDLE_POSITIONS(false); // Drag selection handle 1 to right by 1 char. - x = font.GetStringWidth(WideToUTF16(L"\x05e2")); + x = gfx::Canvas::GetStringWidth(WideToUTF16(L"\x05e2"), font_list); SimulateSelectionHandleDrag(gfx::Point(x, 0), 1); EXPECT_EQ(WideToUTF16(L"c\x05e1"), textfield_->GetSelectedText()); VERIFY_HANDLE_POSITIONS(true); @@ -420,13 +477,13 @@ TEST_F(TouchSelectionControllerImplTest, SelectRectInBidiCallbackTest) { VERIFY_HANDLE_POSITIONS(false); // Drag selection handle 1 to left by 1 char. - x = font.GetStringWidth(WideToUTF16(L"c")); + x = gfx::Canvas::GetStringWidth(WideToUTF16(L"c"), font_list); SimulateSelectionHandleDrag(gfx::Point(-x, 0), 1); EXPECT_EQ(WideToUTF16(L"c\x05e1\x05e2"), textfield_->GetSelectedText()); VERIFY_HANDLE_POSITIONS(true); // Drag selection handle 2 to right by 1 char. - x = font.GetStringWidth(WideToUTF16(L"\x05e2")); + x = gfx::Canvas::GetStringWidth(WideToUTF16(L"\x05e2"), font_list); SimulateSelectionHandleDrag(gfx::Point(x, 0), 2); EXPECT_EQ(WideToUTF16(L"c\x05e1"), textfield_->GetSelectedText()); VERIFY_HANDLE_POSITIONS(false); @@ -444,7 +501,7 @@ TEST_F(TouchSelectionControllerImplTest, // Tap the textfield to invoke selection. ui::GestureEvent tap(ui::ET_GESTURE_TAP, 0, 0, 0, base::TimeDelta(), ui::GestureEventDetails(ui::ET_GESTURE_TAP, 1.0f, 0.0f), 0); - textfield_view_->OnGestureEvent(&tap); + textfield_->OnGestureEvent(&tap); // Select some text such that one handle is hidden. textfield_->SelectRange(gfx::Range(10, textfield_text.length())); @@ -467,9 +524,8 @@ TEST_F(TouchSelectionControllerImplTest, } } -#if defined(USE_AURA) TEST_F(TouchSelectionControllerImplTest, - DoubleTapInTextfieldWithCursorHandleShouldSelectWord) { + DoubleTapInTextfieldWithCursorHandleShouldSelectText) { CreateTextfield(); textfield_->SetText(ASCIIToUTF16("some text")); aura::test::EventGenerator generator( @@ -490,8 +546,302 @@ TEST_F(TouchSelectionControllerImplTest, generator.GestureTapAt(cursor_pos); generator.GestureTapAt(cursor_pos); EXPECT_TRUE(textfield_->HasSelection()); - VERIFY_HANDLE_POSITIONS(false); } -#endif + +// A simple implementation of TouchEditable that allows faking cursor position +// inside its boundaries. +class TestTouchEditable : public ui::TouchEditable { + public: + explicit TestTouchEditable(aura::Window* window) + : window_(window) { + DCHECK(window); + } + + void set_bounds(const gfx::Rect& bounds) { + bounds_ = bounds; + } + + void set_cursor_rect(const gfx::Rect& cursor_rect) { + cursor_rect_ = cursor_rect; + } + + virtual ~TestTouchEditable() {} + + private: + // Overridden from ui::TouchEditable. + virtual void SelectRect( + const gfx::Point& start, const gfx::Point& end) OVERRIDE { + NOTREACHED(); + } + virtual void MoveCaretTo(const gfx::Point& point) OVERRIDE { + NOTREACHED(); + } + virtual void GetSelectionEndPoints(gfx::Rect* p1, gfx::Rect* p2) OVERRIDE { + *p1 = *p2 = cursor_rect_; + } + virtual gfx::Rect GetBounds() OVERRIDE { + return gfx::Rect(bounds_.size()); + } + virtual gfx::NativeView GetNativeView() const OVERRIDE { + return window_; + } + virtual void ConvertPointToScreen(gfx::Point* point) OVERRIDE { + aura::client::ScreenPositionClient* screen_position_client = + aura::client::GetScreenPositionClient(window_->GetRootWindow()); + if (screen_position_client) + screen_position_client->ConvertPointToScreen(window_, point); + } + virtual void ConvertPointFromScreen(gfx::Point* point) OVERRIDE { + aura::client::ScreenPositionClient* screen_position_client = + aura::client::GetScreenPositionClient(window_->GetRootWindow()); + if (screen_position_client) + screen_position_client->ConvertPointFromScreen(window_, point); + } + virtual bool DrawsHandles() OVERRIDE { + return false; + } + virtual void OpenContextMenu(const gfx::Point& anchor) OVERRIDE { + NOTREACHED(); + } + virtual void DestroyTouchSelection() OVERRIDE { + NOTREACHED(); + } + + // Overridden from ui::SimpleMenuModel::Delegate. + virtual bool IsCommandIdChecked(int command_id) const OVERRIDE { + NOTREACHED(); + return false; + } + virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE { + NOTREACHED(); + return false; + } + virtual bool GetAcceleratorForCommandId( + int command_id, + ui::Accelerator* accelerator) OVERRIDE { + NOTREACHED(); + return false; + } + virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE { + NOTREACHED(); + } + + aura::Window* window_; + + // Boundaries of the client view. + gfx::Rect bounds_; + + // Cursor position inside the client view. + gfx::Rect cursor_rect_; + + DISALLOW_COPY_AND_ASSIGN(TestTouchEditable); +}; + +// Tests if the touch editing handle is shown or hidden properly according to +// the cursor position relative to the client boundaries. +TEST_F(TouchSelectionControllerImplTest, + VisibilityOfHandleRegardingClientBounds) { + CreateWidget(); + + TestTouchEditable touch_editable(widget_->GetNativeView()); + scoped_ptr<ui::TouchSelectionController> touch_selection_controller( + ui::TouchSelectionController::create(&touch_editable)); + + touch_editable.set_bounds(gfx::Rect(0, 0, 100, 20)); + + // Put the cursor completely inside the client bounds. Handle should be + // visible. + touch_editable.set_cursor_rect(gfx::Rect(2, 0, 1, 20)); + touch_selection_controller->SelectionChanged(); + EXPECT_TRUE(IsCursorHandleVisibleFor(touch_selection_controller.get())); + + // Move the cursor up such that |kBarMinHeight| pixels are still in the client + // bounds. Handle should still be visible. + touch_editable.set_cursor_rect(gfx::Rect(2, kBarMinHeight - 20, 1, 20)); + touch_selection_controller->SelectionChanged(); + EXPECT_TRUE(IsCursorHandleVisibleFor(touch_selection_controller.get())); + + // Move the cursor up such that less than |kBarMinHeight| pixels are in the + // client bounds. Handle should be hidden. + touch_editable.set_cursor_rect(gfx::Rect(2, kBarMinHeight - 20 - 1, 1, 20)); + touch_selection_controller->SelectionChanged(); + EXPECT_FALSE(IsCursorHandleVisibleFor(touch_selection_controller.get())); + + // Move the Cursor down such that |kBarBottomAllowance| pixels are out of the + // client bounds. Handle should be visible. + touch_editable.set_cursor_rect(gfx::Rect(2, kBarBottomAllowance, 1, 20)); + touch_selection_controller->SelectionChanged(); + EXPECT_TRUE(IsCursorHandleVisibleFor(touch_selection_controller.get())); + + // Move the cursor down such that more than |kBarBottomAllowance| pixels are + // out of the client bounds. Handle should be hidden. + touch_editable.set_cursor_rect(gfx::Rect(2, kBarBottomAllowance + 1, 1, 20)); + touch_selection_controller->SelectionChanged(); + EXPECT_FALSE(IsCursorHandleVisibleFor(touch_selection_controller.get())); + + touch_selection_controller.reset(); +} + +TEST_F(TouchSelectionControllerImplTest, HandlesStackAboveParent) { + ui::EventTarget* root = GetContext(); + ui::EventTargeter* targeter = root->GetEventTargeter(); + + // Create the first window containing a Views::Textfield. + CreateTextfield(); + aura::Window* window1 = textfield_widget_->GetNativeView(); + + // Start touch editing, check that the handle is above the first window, and + // end touch editing. + StartTouchEditing(); + gfx::Point test_point = GetCursorHandleDragPoint(); + ui::MouseEvent test_event1(ui::ET_MOUSE_MOVED, test_point, test_point, + ui::EF_NONE, ui::EF_NONE); + EXPECT_EQ(GetCursorHandleNativeView(), + targeter->FindTargetForEvent(root, &test_event1)); + EndTouchEditing(); + + // Create the second (empty) window over the first one. + CreateWidget(); + aura::Window* window2 = widget_->GetNativeView(); + + // Start touch editing (in the first window) and check that the handle is not + // above the second window. + StartTouchEditing(); + ui::MouseEvent test_event2(ui::ET_MOUSE_MOVED, test_point, test_point, + ui::EF_NONE, ui::EF_NONE); + EXPECT_EQ(window2, targeter->FindTargetForEvent(root, &test_event2)); + + // Move the first window to top and check that the handle is kept above the + // first window. + window1->GetRootWindow()->StackChildAtTop(window1); + ui::MouseEvent test_event3(ui::ET_MOUSE_MOVED, test_point, test_point, + ui::EF_NONE, ui::EF_NONE); + EXPECT_EQ(GetCursorHandleNativeView(), + targeter->FindTargetForEvent(root, &test_event3)); +} + +// A simple implementation of TouchEditingMenuController that enables all +// available commands. +class TestTouchEditingMenuController : public TouchEditingMenuController { + public: + TestTouchEditingMenuController() {} + virtual ~TestTouchEditingMenuController() {} + + // Overriden from TouchEditingMenuController. + virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE { + // Return true, since we want the menu to have all |kMenuCommandCount| + // available commands. + return true; + } + virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE { + NOTREACHED(); + } + virtual void OpenContextMenu() OVERRIDE { + NOTREACHED(); + } + virtual void OnMenuClosed(TouchEditingMenuView* menu) OVERRIDE {} + + private: + DISALLOW_COPY_AND_ASSIGN(TestTouchEditingMenuController); +}; + +// Tests if anchor rect for touch editing quick menu is adjusted correctly based +// on the distance of handles. +TEST_F(TouchSelectionControllerImplTest, QuickMenuAdjustsAnchorRect) { + CreateWidget(); + aura::Window* window = widget_->GetNativeView(); + + scoped_ptr<TestTouchEditingMenuController> quick_menu_controller( + new TestTouchEditingMenuController()); + + // Some arbitrary size for touch editing handle image. + gfx::Size handle_image_size(10, 10); + + // Calculate the width of quick menu. In addition to |kMenuCommandCount| + // commands, there is an item for ellipsis. + int quick_menu_width = (kMenuCommandCount + 1) * kMenuButtonWidth + + kMenuCommandCount; + + // Set anchor rect's width a bit smaller than the quick menu width plus handle + // image width and check that anchor rect's height is adjusted. + gfx::Rect anchor_rect( + 0, 0, quick_menu_width + handle_image_size.width() - 10, 20); + TouchEditingMenuView* quick_menu(TouchEditingMenuView::Create( + quick_menu_controller.get(), anchor_rect, handle_image_size, window)); + anchor_rect.Inset(0, 0, 0, -handle_image_size.height()); + EXPECT_EQ(anchor_rect.ToString(), quick_menu->GetAnchorRect().ToString()); + + // Set anchor rect's width a bit greater than the quick menu width plus handle + // image width and check that anchor rect's height is not adjusted. + anchor_rect = + gfx::Rect(0, 0, quick_menu_width + handle_image_size.width() + 10, 20); + quick_menu = TouchEditingMenuView::Create( + quick_menu_controller.get(), anchor_rect, handle_image_size, window); + EXPECT_EQ(anchor_rect.ToString(), quick_menu->GetAnchorRect().ToString()); + + // Close the widget, hence quick menus, before quick menu controller goes out + // of scope. + widget_->CloseNow(); + widget_ = NULL; +} + +TEST_F(TouchSelectionControllerImplTest, MouseEventDeactivatesTouchSelection) { + CreateTextfield(); + EXPECT_FALSE(GetSelectionController()); + + aura::test::EventGenerator generator( + textfield_widget_->GetNativeView()->GetRootWindow()); + + generator.set_current_location(gfx::Point(5, 5)); + RunPendingMessages(); + + // Start touch editing; then move mouse over the textfield and ensure it + // deactivates touch selection. + StartTouchEditing(); + EXPECT_TRUE(GetSelectionController()); + generator.MoveMouseTo(gfx::Point(5, 10)); + RunPendingMessages(); + EXPECT_FALSE(GetSelectionController()); + + generator.MoveMouseTo(gfx::Point(5, 50)); + RunPendingMessages(); + + // Start touch editing; then move mouse out of the textfield, but inside the + // winow and ensure it deactivates touch selection. + StartTouchEditing(); + EXPECT_TRUE(GetSelectionController()); + generator.MoveMouseTo(gfx::Point(5, 55)); + RunPendingMessages(); + EXPECT_FALSE(GetSelectionController()); + + generator.MoveMouseTo(gfx::Point(5, 500)); + RunPendingMessages(); + + // Start touch editing; then move mouse out of the textfield and window and + // ensure it deactivates touch selection. + StartTouchEditing(); + EXPECT_TRUE(GetSelectionController()); + generator.MoveMouseTo(5, 505); + RunPendingMessages(); + EXPECT_FALSE(GetSelectionController()); +} + +TEST_F(TouchSelectionControllerImplTest, KeyEventDeactivatesTouchSelection) { + CreateTextfield(); + EXPECT_FALSE(GetSelectionController()); + + aura::test::EventGenerator generator( + textfield_widget_->GetNativeView()->GetRootWindow()); + + RunPendingMessages(); + + // Start touch editing; then press a key and ensure it deactivates touch + // selection. + StartTouchEditing(); + EXPECT_TRUE(GetSelectionController()); + generator.PressKey(ui::VKEY_A, 0); + RunPendingMessages(); + EXPECT_FALSE(GetSelectionController()); +} } // namespace views diff --git a/chromium/ui/views/view.cc b/chromium/ui/views/view.cc index 40ad7cce2d9..c8ebd0abb67 100644 --- a/chromium/ui/views/view.cc +++ b/chromium/ui/views/view.cc @@ -16,9 +16,9 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "third_party/skia/include/core/SkRect.h" -#include "ui/base/accessibility/accessibility_types.h" +#include "ui/accessibility/ax_enums.h" +#include "ui/base/cursor/cursor.h" #include "ui/base/dragdrop/drag_drop_types.h" -#include "ui/base/ui_base_switches_util.h" #include "ui/compositor/compositor.h" #include "ui/compositor/layer.h" #include "ui/compositor/layer_animator.h" @@ -39,6 +39,7 @@ #include "ui/views/border.h" #include "ui/views/context_menu_controller.h" #include "ui/views/drag_controller.h" +#include "ui/views/focus/view_storage.h" #include "ui/views/layout/layout_manager.h" #include "ui/views/rect_based_targeting_utils.h" #include "ui/views/views_delegate.h" @@ -47,24 +48,12 @@ #include "ui/views/widget/tooltip_manager.h" #include "ui/views/widget/widget.h" -#if defined(USE_AURA) -#include "ui/base/cursor/cursor.h" -#endif - #if defined(OS_WIN) #include "base/win/scoped_gdi_object.h" #endif namespace { -// Whether to use accelerated compositing when necessary (e.g. when a view has a -// transformation). -#if defined(USE_AURA) -bool use_acceleration_when_possible = true; -#else -bool use_acceleration_when_possible = false; -#endif - #if defined(OS_WIN) const bool kContextMenuOnMousePress = false; #else @@ -76,6 +65,14 @@ const bool kContextMenuOnMousePress = true; // rect-based targeting algorithm. static const float kRectTargetOverlap = 0.6f; +// Default horizontal drag threshold in pixels. +// Same as what gtk uses. +const int kDefaultHorizontalDragThreshold = 8; + +// Default vertical drag threshold in pixels. +// Same as what gtk uses. +const int kDefaultVerticalDragThreshold = 8; + // Returns the top view in |view|'s hierarchy. const views::View* GetHierarchyRoot(const views::View* view) { const views::View* root = view; @@ -90,54 +87,6 @@ namespace views { namespace internal { -// This event handler receives events in the post-target phase and takes care of -// the following: -// - Generates context menu, or initiates drag-and-drop, from gesture events. -class PostEventDispatchHandler : public ui::EventHandler { - public: - explicit PostEventDispatchHandler(View* owner) - : owner_(owner), - touch_dnd_enabled_(switches::IsTouchDragDropEnabled()) { - } - virtual ~PostEventDispatchHandler() {} - - private: - // Overridden from ui::EventHandler: - virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { - DCHECK_EQ(ui::EP_POSTTARGET, event->phase()); - if (event->handled()) - return; - - if (touch_dnd_enabled_) { - if (event->type() == ui::ET_GESTURE_LONG_PRESS && - (!owner_->drag_controller() || - owner_->drag_controller()->CanStartDragForView( - owner_, event->location(), event->location()))) { - if (owner_->DoDrag(*event, event->location(), - ui::DragDropTypes::DRAG_EVENT_SOURCE_TOUCH)) { - event->StopPropagation(); - return; - } - } - } - - if (owner_->context_menu_controller() && - (event->type() == ui::ET_GESTURE_LONG_PRESS || - event->type() == ui::ET_GESTURE_LONG_TAP || - event->type() == ui::ET_GESTURE_TWO_FINGER_TAP)) { - gfx::Point location(event->location()); - View::ConvertPointToScreen(owner_, &location); - owner_->ShowContextMenu(location, ui::MENU_SOURCE_TOUCH); - event->StopPropagation(); - } - } - - View* owner_; - bool touch_dnd_enabled_; - - DISALLOW_COPY_AND_ASSIGN(PostEventDispatchHandler); -}; - } // namespace internal // static @@ -160,6 +109,7 @@ View::View() enabled_(true), notify_enter_exit_on_child_(false), registered_for_visible_bounds_notification_(false), + root_bounds_dirty_(true), clip_insets_(0, 0, 0, 0), needs_layout_(true), flip_canvas_on_paint_for_rtl_ui_(false), @@ -172,15 +122,15 @@ View::View() accessibility_focusable_(false), context_menu_controller_(NULL), drag_controller_(NULL), - post_dispatch_handler_(new internal::PostEventDispatchHandler(this)), native_view_accessibility_(NULL) { - AddPostTargetHandler(post_dispatch_handler_.get()); } View::~View() { if (parent_) parent_->RemoveChildView(this); + ViewStorage::GetInstance()->ViewRemoved(this); + for (Views::const_iterator i(children_.begin()); i != children_.end(); ++i) { (*i)->parent_ = NULL; if (!(*i)->owned_by_client_) @@ -217,8 +167,9 @@ void View::AddChildViewAt(View* view, int index) { // If |view| has a parent, remove it from its parent. View* parent = view->parent_; - const ui::NativeTheme* old_theme = view->GetNativeTheme(); + ui::NativeTheme* old_theme = NULL; if (parent) { + old_theme = view->GetNativeTheme(); if (parent == this) { ReorderChildView(view, index); return; @@ -233,6 +184,16 @@ void View::AddChildViewAt(View* view, int index) { view->parent_ = this; children_.insert(children_.begin() + index, view); + // Instruct the view to recompute its root bounds on next Paint(). + view->SetRootBoundsDirty(true); + + views::Widget* widget = GetWidget(); + if (widget) { + const ui::NativeTheme* new_theme = view->GetNativeTheme(); + if (new_theme != old_theme) + view->PropagateNativeThemeChanged(new_theme); + } + ViewHierarchyChangedDetails details(true, this, view, parent); for (View* v = this; v; v = v->parent_) @@ -240,19 +201,16 @@ void View::AddChildViewAt(View* view, int index) { view->PropagateAddNotifications(details); UpdateTooltip(); - views::Widget* widget = GetWidget(); if (widget) { RegisterChildrenForVisibleBoundsNotification(view); - const ui::NativeTheme* new_theme = widget->GetNativeTheme(); - if (new_theme != old_theme) - PropagateNativeThemeChanged(new_theme); + if (view->visible()) + view->SchedulePaint(); } if (layout_manager_.get()) layout_manager_->ViewAdded(this, view); - if (use_acceleration_when_possible) - ReorderLayers(); + ReorderLayers(); // Make sure the visibility of the child layers are correct. // If any of the parent View is hidden, then the layers of the subtree @@ -286,8 +244,7 @@ void View::ReorderChildView(View* view, int index) { InitFocusSiblings(view, index); children_.insert(children_.begin() + index, view); - if (use_acceleration_when_possible) - ReorderLayers(); + ReorderLayers(); } void View::RemoveChildView(View* view) { @@ -416,7 +373,7 @@ gfx::Rect View::GetBoundsInScreen() const { return gfx::Rect(origin, size()); } -gfx::Size View::GetPreferredSize() { +gfx::Size View::GetPreferredSize() const { if (layout_manager_.get()) return layout_manager_->GetPreferredSize(this); return gfx::Size(); @@ -432,15 +389,15 @@ void View::SizeToPreferredSize() { SetBounds(x(), y(), prefsize.width(), prefsize.height()); } -gfx::Size View::GetMinimumSize() { +gfx::Size View::GetMinimumSize() const { return GetPreferredSize(); } -gfx::Size View::GetMaximumSize() { +gfx::Size View::GetMaximumSize() const { return gfx::Size(); } -int View::GetHeightForWidth(int w) { +int View::GetHeightForWidth(int w) const { if (layout_manager_.get()) return layout_manager_->GetPreferredHeightForWidth(this, w); return GetPreferredSize().height(); @@ -508,6 +465,31 @@ void View::SetTransform(const gfx::Transform& transform) { } void View::SetPaintToLayer(bool paint_to_layer) { + if (paint_to_layer_ == paint_to_layer) + return; + + // If this is a change in state we will also need to update bounds trees. + if (paint_to_layer) { + // Gaining a layer means becoming a paint root. We must remove ourselves + // from our old paint root, if we had one. Traverse up view tree to find old + // paint root. + View* old_paint_root = parent_; + while (old_paint_root && !old_paint_root->IsPaintRoot()) + old_paint_root = old_paint_root->parent_; + + // Remove our and our children's bounds from the old tree. This will also + // mark all of our bounds as dirty. + if (old_paint_root && old_paint_root->bounds_tree_) + RemoveRootBounds(old_paint_root->bounds_tree_.get()); + + } else { + // Losing a layer means we are no longer a paint root, so delete our + // bounds tree and mark ourselves as dirty for future insertion into our + // new paint root's bounds tree. + bounds_tree_.reset(); + SetRootBoundsDirty(true); + } + paint_to_layer_ = paint_to_layer; if (paint_to_layer_ && !layer()) { CreateLayer(); @@ -516,16 +498,6 @@ void View::SetPaintToLayer(bool paint_to_layer) { } } -ui::Layer* View::RecreateLayer() { - ui::Layer* layer = AcquireLayer(); - if (!layer) - return NULL; - - CreateLayer(); - layer_->set_scale_content(layer->scale_content()); - return layer; -} - // RTL positioning ------------------------------------------------------------- gfx::Rect View::GetMirroredBounds() const { @@ -606,14 +578,19 @@ const char* View::GetClassName() const { return kViewClassName; } -View* View::GetAncestorWithClassName(const std::string& name) { - for (View* view = this; view; view = view->parent_) { +const View* View::GetAncestorWithClassName(const std::string& name) const { + for (const View* view = this; view; view = view->parent_) { if (!strcmp(view->GetClassName(), name.c_str())) return view; } return NULL; } +View* View::GetAncestorWithClassName(const std::string& name) { + return const_cast<View*>(const_cast<const View*>(this)-> + GetAncestorWithClassName(name)); +} + const View* View::GetViewByID(int id) const { if (id == id_) return const_cast<View*>(this); @@ -773,40 +750,73 @@ void View::SchedulePaintInRect(const gfx::Rect& rect) { } } -void View::Paint(gfx::Canvas* canvas) { - TRACE_EVENT1("views", "View::Paint", "class", GetClassName()); +void View::Paint(gfx::Canvas* canvas, const CullSet& cull_set) { + // The cull_set may allow us to skip painting without canvas construction or + // even canvas rect intersection. + if (cull_set.ShouldPaint(this)) { + TRACE_EVENT1("views", "View::Paint", "class", GetClassName()); - gfx::ScopedCanvas scoped_canvas(canvas); + gfx::ScopedCanvas scoped_canvas(canvas); - // Paint this View and its children, setting the clip rect to the bounds - // of this View and translating the origin to the local bounds' top left - // point. - // - // Note that the X (or left) position we pass to ClipRectInt takes into - // consideration whether or not the view uses a right-to-left layout so that - // we paint our view in its mirrored position if need be. - gfx::Rect clip_rect = bounds(); - clip_rect.Inset(clip_insets_); - if (parent_) - clip_rect.set_x(parent_->GetMirroredXForRect(clip_rect)); - if (!canvas->ClipRect(clip_rect)) - return; + // Paint this View and its children, setting the clip rect to the bounds + // of this View and translating the origin to the local bounds' top left + // point. + // + // Note that the X (or left) position we pass to ClipRectInt takes into + // consideration whether or not the view uses a right-to-left layout so that + // we paint our view in its mirrored position if need be. + gfx::Rect clip_rect = bounds(); + clip_rect.Inset(clip_insets_); + if (parent_) + clip_rect.set_x(parent_->GetMirroredXForRect(clip_rect)); + canvas->ClipRect(clip_rect); + if (canvas->IsClipEmpty()) + return; + + // Non-empty clip, translate the graphics such that 0,0 corresponds to where + // this view is located (related to its parent). + canvas->Translate(GetMirroredPosition().OffsetFromOrigin()); + canvas->Transform(GetTransform()); - // Non-empty clip, translate the graphics such that 0,0 corresponds to - // where this view is located (related to its parent). - canvas->Translate(GetMirroredPosition().OffsetFromOrigin()); - canvas->Transform(GetTransform()); + // If we are a paint root, we need to construct our own CullSet object for + // propagation to our children. + if (IsPaintRoot()) { + if (!bounds_tree_) + bounds_tree_.reset(new BoundsTree(2, 5)); - PaintCommon(canvas); + // Recompute our bounds tree as needed. + UpdateRootBounds(bounds_tree_.get(), gfx::Vector2d()); + + // Grab the clip rect from the supplied canvas to use as the query rect. + gfx::Rect canvas_bounds; + if (!canvas->GetClipBounds(&canvas_bounds)) { + NOTREACHED() << "Failed to get clip bounds from the canvas!"; + return; + } + + // Now query our bounds_tree_ for a set of damaged views that intersect + // our canvas bounds. + scoped_ptr<base::hash_set<intptr_t> > damaged_views( + new base::hash_set<intptr_t>()); + bounds_tree_->AppendIntersectingRecords( + canvas_bounds, damaged_views.get()); + // Construct a CullSet to wrap the damaged views set, it will delete it + // for us on scope exit. + CullSet paint_root_cull_set(damaged_views.Pass()); + // Paint all descendents using our new cull set. + PaintCommon(canvas, paint_root_cull_set); + } else { + // Not a paint root, so we can proceed as normal. + PaintCommon(canvas, cull_set); + } + } } void View::set_background(Background* b) { background_.reset(b); } -void View::set_border(Border* b) { - border_.reset(b); -} +void View::SetBorder(scoped_ptr<Border> b) { border_ = b.Pass(); } ui::ThemeProvider* View::GetThemeProvider() const { const Widget* widget = GetWidget(); @@ -818,18 +828,6 @@ const ui::NativeTheme* View::GetNativeTheme() const { return widget ? widget->GetNativeTheme() : ui::NativeTheme::instance(); } -// Accelerated Painting -------------------------------------------------------- - -// static -void View::set_use_acceleration_when_possible(bool use) { - use_acceleration_when_possible = use; -} - -// static -bool View::get_use_acceleration_when_possible() { - return use_acceleration_when_possible; -} - // Input ----------------------------------------------------------------------- View* View::GetEventHandlerForPoint(const gfx::Point& point) { @@ -852,6 +850,9 @@ View* View::GetEventHandlerForRect(const gfx::Rect& rect) { for (int i = child_count() - 1; i >= 0; --i) { View* child = child_at(i); + if (!child->CanProcessEventsWithinSubtree()) + continue; + // Ignore any children which are invisible or do not intersect |rect|. if (!child->visible()) continue; @@ -907,8 +908,12 @@ View* View::GetEventHandlerForRect(const gfx::Rect& rect) { return rect_view ? rect_view : point_view; } +bool View::CanProcessEventsWithinSubtree() const { + return true; +} + View* View::GetTooltipHandlerForPoint(const gfx::Point& point) { - if (!HitTestPoint(point)) + if (!HitTestPoint(point) || !CanProcessEventsWithinSubtree()) return NULL; // Walk the child Views recursively looking for the View that most @@ -929,16 +934,11 @@ View* View::GetTooltipHandlerForPoint(const gfx::Point& point) { gfx::NativeCursor View::GetCursor(const ui::MouseEvent& event) { #if defined(OS_WIN) -#if defined(USE_AURA) static ui::Cursor arrow; if (!arrow.platform()) arrow.SetPlatformCursor(LoadCursor(NULL, IDC_ARROW)); return arrow; #else - static HCURSOR arrow = LoadCursor(NULL, IDC_ARROW); - return arrow; -#endif -#else return gfx::kNullCursor; #endif } @@ -955,18 +955,11 @@ bool View::HitTestRect(const gfx::Rect& rect) const { if (!views::UsePointBasedTargeting(rect)) source = HIT_TEST_SOURCE_TOUCH; GetHitTestMask(source, &mask); -#if defined(USE_AURA) - // TODO: should we use this every where? SkRegion clip_region; clip_region.setRect(0, 0, width(), height()); SkRegion mask_region; return mask_region.setPath(mask, clip_region) && mask_region.intersects(RectToSkIRect(rect)); -#elif defined(OS_WIN) - base::win::ScopedRegion rgn(mask.CreateNativeRegion()); - const RECT r(rect.ToRECT()); - return RectInRegion(rgn, &r) != 0; -#endif } // No mask, but inside our bounds. return true; @@ -1070,6 +1063,8 @@ void View::OnMouseEvent(ui::MouseEvent* event) { break; case ui::ET_MOUSE_ENTERED: + if (event->flags() & ui::EF_TOUCH_ACCESSIBILITY) + NotifyAccessibilityEvent(ui::AX_EVENT_HOVER, true); OnMouseEntered(*event); break; @@ -1086,6 +1081,7 @@ void View::OnScrollEvent(ui::ScrollEvent* event) { } void View::OnTouchEvent(ui::TouchEvent* event) { + NOTREACHED() << "Views should not receive touch events."; } void View::OnGestureEvent(ui::GestureEvent* event) { @@ -1105,6 +1101,13 @@ const InputMethod* View::GetInputMethod() const { return widget ? widget->GetInputMethod() : NULL; } +scoped_ptr<ui::EventTargeter> +View::SetEventTargeter(scoped_ptr<ui::EventTargeter> targeter) { + scoped_ptr<ui::EventTargeter> old_targeter = targeter_.Pass(); + targeter_ = targeter.Pass(); + return old_targeter.Pass(); +} + bool View::CanAcceptEvent(const ui::Event& event) { return IsDrawn(); } @@ -1119,7 +1122,16 @@ scoped_ptr<ui::EventTargetIterator> View::GetChildIterator() const { } ui::EventTargeter* View::GetEventTargeter() { - return NULL; + return targeter_.get(); +} + +const ui::EventTargeter* View::GetEventTargeter() const { + return targeter_.get(); +} + +void View::ConvertEventToTarget(ui::EventTarget* target, + ui::LocatedEvent* event) { + event->ConvertLocationToTarget(this, static_cast<View*>(target)); } // Accelerators ---------------------------------------------------------------- @@ -1252,7 +1264,7 @@ FocusTraversable* View::GetPaneFocusTraversable() { // Tooltips -------------------------------------------------------------------- -bool View::GetTooltipText(const gfx::Point& p, string16* tooltip) const { +bool View::GetTooltipText(const gfx::Point& p, base::string16* tooltip) const { return false; } @@ -1326,12 +1338,12 @@ gfx::NativeViewAccessible View::GetNativeViewAccessible() { } void View::NotifyAccessibilityEvent( - ui::AccessibilityTypes::Event event_type, + ui::AXEvent event_type, bool send_native_event) { if (ViewsDelegate::views_delegate) ViewsDelegate::views_delegate->NotifyAccessibilityEvent(this, event_type); - if (send_native_event) { + if (send_native_event && GetWidget()) { if (!native_view_accessibility_) native_view_accessibility_ = NativeViewAccessibility::Create(this); if (native_view_accessibility_) @@ -1402,11 +1414,11 @@ void View::NativeViewHierarchyChanged() { // Painting -------------------------------------------------------------------- -void View::PaintChildren(gfx::Canvas* canvas) { +void View::PaintChildren(gfx::Canvas* canvas, const CullSet& cull_set) { TRACE_EVENT1("views", "View::PaintChildren", "class", GetClassName()); for (int i = 0, count = child_count(); i < count; ++i) if (!child_at(i)->layer()) - child_at(i)->Paint(canvas); + child_at(i)->Paint(canvas, cull_set); } void View::OnPaint(gfx::Canvas* canvas) { @@ -1433,6 +1445,10 @@ void View::OnPaintBorder(gfx::Canvas* canvas) { } } +bool View::IsPaintRoot() { + return paint_to_layer_ || !parent_; +} + // Accelerated Painting -------------------------------------------------------- void View::SetFillsBoundsOpaquely(bool fills_bounds_opaquely) { @@ -1484,8 +1500,6 @@ void View::MoveLayerToParent(ui::Layer* parent_layer, } void View::UpdateLayerVisibility() { - if (!use_acceleration_when_possible) - return; bool visible = visible_; for (const View* v = parent_; visible && v && !v->layer(); v = v->parent_) visible = v->visible(); @@ -1517,7 +1531,7 @@ void View::UpdateChildLayerBounds(const gfx::Vector2d& offset) { void View::OnPaintLayer(gfx::Canvas* canvas) { if (!layer() || !layer()->fills_bounds_opaquely()) canvas->DrawColor(SK_ColorBLACK, SkXfermode::kClear_Mode); - PaintCommon(canvas); + PaintCommon(canvas, CullSet()); } void View::OnDeviceScaleFactorChanged(float device_scale_factor) { @@ -1562,7 +1576,7 @@ void View::ReorderChildLayers(ui::Layer* parent_layer) { // Iterate backwards through the children so that a child with a layer // which is further to the back is stacked above one which is further to // the front. - for (Views::const_reverse_iterator it(children_.rbegin()); + for (Views::reverse_iterator it(children_.rbegin()); it != children_.rend(); ++it) { (*it)->ReorderChildLayers(parent_layer); } @@ -1597,7 +1611,7 @@ void View::OnFocus() { // TODO(beng): Investigate whether it's possible for us to move this to // Focus(). // Notify assistive technologies of the focus change. - NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_FOCUS, true); + NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true); } void View::OnBlur() { @@ -1648,6 +1662,18 @@ bool View::InDrag() { return widget ? widget->dragged_view() == this : false; } +int View::GetHorizontalDragThreshold() { + // TODO(jennyz): This value may need to be adjusted for different platforms + // and for different display density. + return kDefaultHorizontalDragThreshold; +} + +int View::GetVerticalDragThreshold() { + // TODO(jennyz): This value may need to be adjusted for different platforms + // and for different display density. + return kDefaultVerticalDragThreshold; +} + // Debugging ------------------------------------------------------------------- #if !defined(NDEBUG) @@ -1722,7 +1748,7 @@ std::string View::DoPrintViewGraph(bool first, View* view_with_children) { if (!parent_) result.append(", shape=box"); if (layer()) { - if (layer()->texture()) + if (layer()->has_external_content()) result.append(", color=green"); else result.append(", color=red"); @@ -1753,7 +1779,6 @@ std::string View::DoPrintViewGraph(bool first, View* view_with_children) { return result; } - #endif //////////////////////////////////////////////////////////////////////////////// @@ -1790,7 +1815,7 @@ void View::SchedulePaintBoundsChanged(SchedulePaintType type) { } } -void View::PaintCommon(gfx::Canvas* canvas) { +void View::PaintCommon(gfx::Canvas* canvas, const CullSet& cull_set) { if (!visible_) return; @@ -1809,7 +1834,7 @@ void View::PaintCommon(gfx::Canvas* canvas) { OnPaint(canvas); } - PaintChildren(canvas); + PaintChildren(canvas, cull_set); } // Tree operations ------------------------------------------------------------- @@ -1820,6 +1845,7 @@ void View::DoRemoveChildView(View* view, bool delete_removed_view, View* new_parent) { DCHECK(view); + const Views::iterator i(std::find(children_.begin(), children_.end(), view)); scoped_ptr<View> view_to_be_deleted; if (i != children_.end()) { @@ -1833,8 +1859,19 @@ void View::DoRemoveChildView(View* view, next_focusable->previous_focusable_view_ = prev_focusable; } - if (GetWidget()) + if (GetWidget()) { UnregisterChildrenForVisibleBoundsNotification(view); + if (view->visible()) + view->SchedulePaint(); + GetWidget()->NotifyWillRemoveView(view); + } + + // Remove the bounds of this child and any of its descendants from our + // paint root bounds tree. + BoundsTree* bounds_tree = GetBoundsTreeFromPaintRoot(); + if (bounds_tree) + view->RemoveRootBounds(bounds_tree); + view->PropagateRemoveNotifications(this, new_parent); view->parent_ = NULL; view->UpdateLayerVisibility(); @@ -1898,11 +1935,9 @@ void View::ViewHierarchyChangedImpl( // Make sure the layers belonging to the subtree rooted at |child| get // removed from layers that do not belong in the same subtree. OrphanLayers(); - if (use_acceleration_when_possible) { - Widget* widget = GetWidget(); - if (widget) - widget->UpdateRootLayers(); - } + Widget* widget = GetWidget(); + if (widget) + widget->UpdateRootLayers(); } ViewHierarchyChanged(details); @@ -1928,6 +1963,12 @@ void View::VisibilityChangedImpl(View* starting_from, bool is_visible) { } void View::BoundsChanged(const gfx::Rect& previous_bounds) { + // Mark our bounds as dirty for the paint root, also see if we need to + // recompute our children's bounds due to origin change. + bool origin_changed = + previous_bounds.OffsetFromOrigin() != bounds_.OffsetFromOrigin(); + SetRootBoundsDirty(origin_changed); + if (visible_) { // Paint the new bounds. SchedulePaintBoundsChanged( @@ -1935,28 +1976,18 @@ void View::BoundsChanged(const gfx::Rect& previous_bounds) { SCHEDULE_PAINT_SIZE_CHANGED); } - if (use_acceleration_when_possible) { - if (layer()) { - if (parent_) { - SetLayerBounds(GetLocalBounds() + - gfx::Vector2d(GetMirroredX(), y()) + - parent_->CalculateOffsetToAncestorWithLayer(NULL)); - } else { - SetLayerBounds(bounds_); - } - // TODO(beng): this seems redundant with the SchedulePaint at the top of - // this function. explore collapsing. - if (previous_bounds.size() != bounds_.size() && - !layer()->layer_updated_externally()) { - // If our bounds have changed then we need to update the complete - // texture. - layer()->SchedulePaint(GetLocalBounds()); - } + if (layer()) { + if (parent_) { + SetLayerBounds(GetLocalBounds() + + gfx::Vector2d(GetMirroredX(), y()) + + parent_->CalculateOffsetToAncestorWithLayer(NULL)); } else { - // If our bounds have changed, then any descendant layer bounds may - // have changed. Update them accordingly. - UpdateChildLayerBounds(CalculateOffsetToAncestorWithLayer(NULL)); + SetLayerBounds(bounds_); } + } else { + // If our bounds have changed, then any descendant layer bounds may have + // changed. Update them accordingly. + UpdateChildLayerBounds(CalculateOffsetToAncestorWithLayer(NULL)); } OnBoundsChanged(previous_bounds); @@ -2034,6 +2065,69 @@ void View::SetLayerBounds(const gfx::Rect& bounds) { layer()->SetBounds(bounds); } +void View::SetRootBoundsDirty(bool origin_changed) { + root_bounds_dirty_ = true; + + if (origin_changed) { + // Inform our children that their root bounds are dirty, as their relative + // coordinates in paint root space have changed since ours have changed. + for (Views::const_iterator i(children_.begin()); i != children_.end(); + ++i) { + if (!(*i)->IsPaintRoot()) + (*i)->SetRootBoundsDirty(origin_changed); + } + } +} + +void View::UpdateRootBounds(BoundsTree* tree, const gfx::Vector2d& offset) { + // No need to recompute bounds if we haven't flagged ours as dirty. + TRACE_EVENT1("views", "View::UpdateRootBounds", "class", GetClassName()); + + // Add our own offset to the provided offset, for our own bounds update and + // for propagation to our children if needed. + gfx::Vector2d view_offset = offset + GetMirroredBounds().OffsetFromOrigin(); + + // If our bounds have changed we must re-insert our new bounds to the tree. + if (root_bounds_dirty_) { + root_bounds_dirty_ = false; + gfx::Rect bounds( + view_offset.x(), view_offset.y(), bounds_.width(), bounds_.height()); + tree->Insert(bounds, reinterpret_cast<intptr_t>(this)); + } + + // Update our children's bounds if needed. + for (Views::const_iterator i(children_.begin()); i != children_.end(); ++i) { + // We don't descend in to layer views for bounds recomputation, as they + // manage their own RTree as paint roots. + if (!(*i)->IsPaintRoot()) + (*i)->UpdateRootBounds(tree, view_offset); + } +} + +void View::RemoveRootBounds(BoundsTree* tree) { + tree->Remove(reinterpret_cast<intptr_t>(this)); + + root_bounds_dirty_ = true; + + for (Views::const_iterator i(children_.begin()); i != children_.end(); ++i) { + if (!(*i)->IsPaintRoot()) + (*i)->RemoveRootBounds(tree); + } +} + +View::BoundsTree* View::GetBoundsTreeFromPaintRoot() { + BoundsTree* bounds_tree = bounds_tree_.get(); + View* paint_root = this; + while (!bounds_tree && !paint_root->IsPaintRoot()) { + // Assumption is that if IsPaintRoot() is false then parent_ is valid. + DCHECK(paint_root); + paint_root = paint_root->parent_; + bounds_tree = paint_root->bounds_tree_.get(); + } + + return bounds_tree; +} + // Transformations ------------------------------------------------------------- bool View::GetTransformRelativeTo(const View* ancestor, @@ -2101,11 +2195,10 @@ void View::CreateLayer() { for (int i = 0, count = child_count(); i < count; ++i) child_at(i)->UpdateChildLayerVisibility(true); - layer_ = new ui::Layer(); - layer_owner_.reset(layer_); - layer_->set_delegate(this); + SetLayer(new ui::Layer()); + layer()->set_delegate(this); #if !defined(NDEBUG) - layer_->set_name(GetClassName()); + layer()->set_name(GetClassName()); #endif UpdateParentLayers(); @@ -2146,11 +2239,11 @@ void View::OrphanLayers() { } void View::ReparentLayer(const gfx::Vector2d& offset, ui::Layer* parent_layer) { - layer_->SetBounds(GetLocalBounds() + offset); + layer()->SetBounds(GetLocalBounds() + offset); DCHECK_NE(layer(), parent_layer); if (parent_layer) parent_layer->Add(layer()); - layer_->SchedulePaint(GetLocalBounds()); + layer()->SchedulePaint(GetLocalBounds()); MoveLayerToParent(layer(), gfx::Point()); } @@ -2163,8 +2256,7 @@ void View::DestroyLayer() { new_parent->Add(children[i]); } - layer_ = NULL; - layer_owner_.reset(); + LayerOwner::DestroyLayer(); if (new_parent) ReorderLayers(); @@ -2189,6 +2281,15 @@ bool View::ProcessMousePressed(const ui::MouseEvent& event) { context_menu_controller_ : 0; View::DragInfo* drag_info = GetDragInfo(); + // TODO(sky): for debugging 360238. + int storage_id = 0; + if (event.IsOnlyRightMouseButton() && context_menu_controller && + kContextMenuOnMousePress && HitTestPoint(event.location())) { + ViewStorage* view_storage = ViewStorage::GetInstance(); + storage_id = view_storage->CreateStorageID(); + view_storage->StoreView(storage_id, this); + } + const bool enabled = enabled_; const bool result = OnMousePressed(event); @@ -2201,6 +2302,8 @@ bool View::ProcessMousePressed(const ui::MouseEvent& event) { // from mouse pressed. gfx::Point location(event.location()); if (HitTestPoint(location)) { + if (storage_id != 0) + CHECK_EQ(this, ViewStorage::GetInstance()->RetrieveView(storage_id)); ConvertPointToScreen(this, &location); ShowContextMenu(location, ui::MENU_SOURCE_MOUSE); return true; diff --git a/chromium/ui/views/view.h b/chromium/ui/views/view.h index dd298edc0a7..93cbd481ddb 100644 --- a/chromium/ui/views/view.h +++ b/chromium/ui/views/view.h @@ -16,8 +16,8 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "build/build_config.h" +#include "ui/accessibility/ax_enums.h" #include "ui/base/accelerators/accelerator.h" -#include "ui/base/accessibility/accessibility_types.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/drop_target_event.h" #include "ui/base/dragdrop/os_exchange_data.h" @@ -26,10 +26,13 @@ #include "ui/compositor/layer_owner.h" #include "ui/events/event.h" #include "ui/events/event_target.h" +#include "ui/events/event_targeter.h" +#include "ui/gfx/geometry/r_tree.h" #include "ui/gfx/insets.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/rect.h" #include "ui/gfx/vector2d.h" +#include "ui/views/cull_set.h" #include "ui/views/views_export.h" #if defined(OS_WIN) @@ -46,7 +49,7 @@ class Transform; } namespace ui { -struct AccessibleViewState; +struct AXViewState; class Compositor; class Layer; class NativeTheme; @@ -70,6 +73,7 @@ class ScrollView; class Widget; namespace internal { +class PreEventDispatchHandler; class PostEventDispatchHandler; class RootView; } @@ -106,8 +110,9 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, public: typedef std::vector<View*> Views; - // TODO(tdanderson,sadrul): Becomes obsolete with the refactoring of the - // event targeting logic for views and windows. + // TODO(tdanderson): Becomes obsolete with the refactoring of the event + // targeting logic for views and windows. See + // crbug.com/355425. // Specifies the source of the region used in a hit test. // HIT_TEST_SOURCE_MOUSE indicates the hit test is being performed with a // single point and HIT_TEST_SOURCE_TOUCH indicates the hit test is being @@ -260,24 +265,24 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, virtual int GetBaseline() const; // Get the size the View would like to be, if enough space were available. - virtual gfx::Size GetPreferredSize(); + virtual gfx::Size GetPreferredSize() const; // Convenience method that sizes this view to its preferred size. void SizeToPreferredSize(); // Gets the minimum size of the view. View's implementation invokes // GetPreferredSize. - virtual gfx::Size GetMinimumSize(); + virtual gfx::Size GetMinimumSize() const; // Gets the maximum size of the view. Currently only used for sizing shell // windows. - virtual gfx::Size GetMaximumSize(); + virtual gfx::Size GetMaximumSize() const; // Return the height necessary to display this view with the provided width. // View's implementation returns the value from getPreferredSize.cy. // Override if your View's preferred height depends upon the width (such // as with Labels). - virtual int GetHeightForWidth(int w); + virtual int GetHeightForWidth(int w) const; // Set whether this view is visible. Painting is scheduled as needed. virtual void SetVisible(bool visible); @@ -323,13 +328,6 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // Compositor. void SetPaintToLayer(bool paint_to_layer); - // Recreates a layer for the view and returns the old layer. After this call, - // the View no longer has a pointer to the old layer (so it won't be able to - // update the old layer or destroy it). The caller must free the returned - // layer. - // Returns NULL and does not recreate layer if view does not own its layer. - ui::Layer* RecreateLayer() WARN_UNUSED_RESULT; - // RTL positioning ----------------------------------------------------------- // Methods for accessing the bounds and position of the view, relative to its @@ -408,6 +406,7 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // Returns the first ancestor, starting at this, whose class name is |name|. // Returns null if no ancestor has the class name |name|. + const View* GetAncestorWithClassName(const std::string& name) const; View* GetAncestorWithClassName(const std::string& name); // Recursively descends the view tree starting at this view, and returns @@ -505,7 +504,7 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // for View coordinates and language direction as required, allows the View // to paint itself via the various OnPaint*() event handlers and then paints // the hierarchy beneath it. - virtual void Paint(gfx::Canvas* canvas); + virtual void Paint(gfx::Canvas* canvas, const CullSet& cull_set); // The background object is owned by this object and may be NULL. void set_background(Background* b); @@ -513,12 +512,12 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, Background* background() { return background_.get(); } // The border object is owned by this object and may be NULL. - void set_border(Border* b); + virtual void SetBorder(scoped_ptr<Border> b); const Border* border() const { return border_.get(); } Border* border() { return border_.get(); } // Get the theme provider from the parent widget. - virtual ui::ThemeProvider* GetThemeProvider() const; + ui::ThemeProvider* GetThemeProvider() const; // Returns the NativeTheme to use for this View. This calls through to // GetNativeTheme() on the Widget this View is in. If this View is not in a @@ -557,16 +556,15 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, flip_canvas_on_paint_for_rtl_ui_ = enable; } - // Accelerated painting ------------------------------------------------------ - - // Enable/Disable accelerated compositing. - static void set_use_acceleration_when_possible(bool use); - static bool get_use_acceleration_when_possible(); - // Input --------------------------------------------------------------------- // The points, rects, mouse locations, and touch locations in the following // functions are in the view's coordinates, except for a RootView. + // TODO(tdanderson): GetEventHandlerForPoint() and GetEventHandlerForRect() + // will be removed once their logic is moved into + // ViewTargeter and its derived classes. See + // crbug.com/355425. + // Convenience functions which calls into GetEventHandler() with // a 1x1 rect centered at |point|. View* GetEventHandlerForPoint(const gfx::Point& point); @@ -592,6 +590,10 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // the cursor is a shared resource. virtual gfx::NativeCursor GetCursor(const ui::MouseEvent& event); + // TODO(tdanderson): HitTestPoint() and HitTestRect() will be removed once + // their logic is moved into ViewTargeter and its + // derived classes. See crbug.com/355425. + // A convenience function which calls HitTestRect() with a rect of size // 1x1 and an origin of |point|. bool HitTestPoint(const gfx::Point& point) const; @@ -599,6 +601,10 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // Tests whether |rect| intersects this view's bounds. virtual bool HitTestRect(const gfx::Rect& rect) const; + // Returns true if this view or any of its descendants are permitted to + // be the target of an event. + virtual bool CanProcessEventsWithinSubtree() const; + // Returns true if the mouse cursor is over |view| and mouse events are // enabled. bool IsMouseHovered(); @@ -713,17 +719,26 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, virtual InputMethod* GetInputMethod(); virtual const InputMethod* GetInputMethod() const; + // Sets a new event-targeter for the view, and returns the previous + // event-targeter. + scoped_ptr<ui::EventTargeter> SetEventTargeter( + scoped_ptr<ui::EventTargeter> targeter); + // Overridden from ui::EventTarget: virtual bool CanAcceptEvent(const ui::Event& event) OVERRIDE; virtual ui::EventTarget* GetParentTarget() OVERRIDE; virtual scoped_ptr<ui::EventTargetIterator> GetChildIterator() const OVERRIDE; virtual ui::EventTargeter* GetEventTargeter() OVERRIDE; + virtual void ConvertEventToTarget(ui::EventTarget* target, + ui::LocatedEvent* event) OVERRIDE; + + const ui::EventTargeter* GetEventTargeter() const; // 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 OnTouchEvent(ui::TouchEvent* event) OVERRIDE FINAL; virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; // Accelerators -------------------------------------------------------------- @@ -773,9 +788,6 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // not get the focus. void SetFocusable(bool focusable); - // Returns true if this view is capable of taking focus. - bool focusable() const { return focusable_ && enabled_ && visible_; } - // Returns true if this view is |focusable_|, |enabled_| and drawn. virtual bool IsFocusable() const; @@ -833,7 +845,8 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // Any time the tooltip text that a View is displaying changes, it must // invoke TooltipTextChanged. // |p| provides the coordinates of the mouse (relative to this view). - virtual bool GetTooltipText(const gfx::Point& p, string16* tooltip) const; + virtual bool GetTooltipText(const gfx::Point& p, + base::string16* tooltip) const; // Returns the location (relative to this View) for the text on the tooltip // to display. If false is returned (the default), the tooltip is placed at @@ -936,7 +949,7 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // Accessibility ------------------------------------------------------------- // Modifies |state| to reflect the current accessible state of this view. - virtual void GetAccessibleState(ui::AccessibleViewState* state) { } + virtual void GetAccessibleState(ui::AXViewState* state) { } // Returns an instance of the native accessibility interface for this view. virtual gfx::NativeViewAccessible GetNativeViewAccessible(); @@ -947,7 +960,7 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // cases where the view is a native control that's already sending a // native accessibility event and the duplicate event would cause // problems. - void NotifyAccessibilityEvent(ui::AccessibilityTypes::Event event_type, + void NotifyAccessibilityEvent(ui::AXEvent event_type, bool send_native_event); // Scrolling ----------------------------------------------------------------- @@ -1036,6 +1049,8 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // changed. The default implementation calls SchedulePaint() on this View. virtual void OnEnabledChanged(); + bool needs_layout() const { return needs_layout_; } + // Tree operations ----------------------------------------------------------- // This method is invoked when the tree changes. @@ -1069,7 +1084,7 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // Responsible for calling Paint() on child Views. Override to control the // order child Views are painted. - virtual void PaintChildren(gfx::Canvas* canvas); + virtual void PaintChildren(gfx::Canvas* canvas, const CullSet& cull_set); // Override to provide rendering in any part of the View's bounds. Typically // this is the "contents" of the view. If you override this method you will @@ -1084,6 +1099,11 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // Override to paint a border not specified by SetBorder(). virtual void OnPaintBorder(gfx::Canvas* canvas); + // Returns true if this View is the root for paint events, and should + // therefore maintain a |bounds_tree_| member and use it for paint damage rect + // calculations. + virtual bool IsPaintRoot(); + // Accelerated painting ------------------------------------------------------ // Returns the offset from this view to the nearest ancestor with a layer. If @@ -1141,6 +1161,10 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // Focus --------------------------------------------------------------------- + // Returns last value passed to SetFocusable(). Use IsFocusable() to determine + // if a view can take focus right now. + bool focusable() const { return focusable_; } + // Override to be notified when focus has changed either to or from this View. virtual void OnFocus(); virtual void OnBlur(); @@ -1218,11 +1242,14 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, #endif private: + friend class internal::PreEventDispatchHandler; friend class internal::PostEventDispatchHandler; friend class internal::RootView; friend class FocusManager; friend class Widget; + typedef gfx::RTree<intptr_t> BoundsTree; + // Painting ----------------------------------------------------------------- enum SchedulePaintType { @@ -1239,7 +1266,7 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // Common Paint() code shared by accelerated and non-accelerated code paths to // invoke OnPaint() on the View. - void PaintCommon(gfx::Canvas* canvas); + void PaintCommon(gfx::Canvas* canvas, const CullSet& cull_set); // Tree operations ----------------------------------------------------------- @@ -1308,6 +1335,25 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // Sets the layer's bounds given in DIP coordinates. void SetLayerBounds(const gfx::Rect& bounds_in_dip); + // Sets the bit indicating that the cached bounds for this object within the + // root view bounds tree are no longer valid. If |origin_changed| is true sets + // the same bit for all of our children as well. + void SetRootBoundsDirty(bool origin_changed); + + // If needed, updates the bounds rectangle in paint root coordinate space + // in the supplied RTree. Recurses to children for recomputation as well. + void UpdateRootBounds(BoundsTree* bounds_tree, const gfx::Vector2d& offset); + + // Remove self and all children from the supplied bounds tree. This is used, + // for example, when a view gets a layer and therefore becomes paint root. It + // needs to remove all references to itself and its children from any previous + // paint root that may have been tracking it. + void RemoveRootBounds(BoundsTree* bounds_tree); + + // Traverse up the View hierarchy to the first ancestor that is a paint root + // and return a pointer to its |bounds_tree_| or NULL if no tree is found. + BoundsTree* GetBoundsTreeFromPaintRoot(); + // Transformations ----------------------------------------------------------- // Returns in |transform| the transform to get from coordinates of |ancestor| @@ -1480,6 +1526,15 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // List of descendants wanting notification when their visible bounds change. scoped_ptr<Views> descendants_to_notify_; + // True if the bounds on this object have changed since the last time the + // paint root view constructed the spatial database. + bool root_bounds_dirty_; + + // If this View IsPaintRoot() then this will be a pointer to a spatial data + // structure where we will keep the bounding boxes of all our children, for + // efficient paint damage rectangle intersection. + scoped_ptr<BoundsTree> bounds_tree_; + // Transformations ----------------------------------------------------------- // Clipping parameters. skia transformation matrix does not give us clipping. @@ -1551,7 +1606,7 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // Input -------------------------------------------------------------------- - scoped_ptr<internal::PostEventDispatchHandler> post_dispatch_handler_; + scoped_ptr<ui::EventTargeter> targeter_; // Accessibility ------------------------------------------------------------- diff --git a/chromium/ui/views/view_aura.cc b/chromium/ui/views/view_aura.cc deleted file mode 100644 index 006f1da2840..00000000000 --- a/chromium/ui/views/view_aura.cc +++ /dev/null @@ -1,33 +0,0 @@ -// 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/view.h" - -namespace { - -// Default horizontal drag threshold in pixels. -// Same as what gtk uses. -const int kDefaultHorizontalDragThreshold = 8; - -// Default vertical drag threshold in pixels. -// Same as what gtk uses. -const int kDefaultVerticalDragThreshold = 8; - -} // namespace - -namespace views { - -int View::GetHorizontalDragThreshold() { - // TODO(jennyz): This value may need to be adjusted for different platforms - // and for different display density. - return kDefaultHorizontalDragThreshold; -} - -int View::GetVerticalDragThreshold() { - // TODO(jennyz): This value may need to be adjusted for different platforms - // and for different display density. - return kDefaultVerticalDragThreshold; -} - -} // namespace views diff --git a/chromium/ui/views/view_constants_aura.cc b/chromium/ui/views/view_constants_aura.cc index 977050fb4e8..caf964a3119 100644 --- a/chromium/ui/views/view_constants_aura.cc +++ b/chromium/ui/views/view_constants_aura.cc @@ -13,4 +13,6 @@ namespace views { DEFINE_WINDOW_PROPERTY_KEY(views::View*, kHostViewKey, NULL); +DEFINE_WINDOW_PROPERTY_KEY(bool, kDesktopRootWindow, false); + } // namespace views diff --git a/chromium/ui/views/view_constants_aura.h b/chromium/ui/views/view_constants_aura.h index e0d718d2e85..1ca28207617 100644 --- a/chromium/ui/views/view_constants_aura.h +++ b/chromium/ui/views/view_constants_aura.h @@ -17,6 +17,10 @@ class View; // have the same parent widget as the window on which the property is set. VIEWS_EXPORT extern const aura::WindowProperty<View*>* const kHostViewKey; +// A property key to indicate if a window is a desktop root window or not. +// This property is currently set during creation of desktop root window. +VIEWS_EXPORT extern const aura::WindowProperty<bool>* const kDesktopRootWindow; + } // namespace views #endif // UI_VIEWS_VIEW_CONSTANTS_AURA_H_ diff --git a/chromium/ui/views/view_model.cc b/chromium/ui/views/view_model.cc index 5f492f5380d..a0a75496450 100644 --- a/chromium/ui/views/view_model.cc +++ b/chromium/ui/views/view_model.cc @@ -33,6 +33,11 @@ void ViewModel::Remove(int index) { } void ViewModel::Move(int index, int target_index) { + DCHECK_LT(index, static_cast<int>(entries_.size())); + DCHECK_GE(index, 0); + DCHECK_LT(target_index, static_cast<int>(entries_.size())); + DCHECK_GE(target_index, 0); + if (index == target_index) return; Entry entry(entries_[index]); diff --git a/chromium/ui/views/view_targeter.cc b/chromium/ui/views/view_targeter.cc new file mode 100644 index 00000000000..31babfb88c0 --- /dev/null +++ b/chromium/ui/views/view_targeter.cc @@ -0,0 +1,79 @@ +// Copyright 2014 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/view_targeter.h" + +#include "ui/events/event_target.h" +#include "ui/views/focus/focus_manager.h" +#include "ui/views/view.h" + +namespace views { + +ViewTargeter::ViewTargeter() {} +ViewTargeter::~ViewTargeter() {} + +gfx::RectF ViewTargeter::BoundsForEvent(const ui::LocatedEvent& event) const { + gfx::RectF event_bounds(event.location_f(), gfx::SizeF(1, 1)); + if (event.IsGestureEvent()) { + const ui::GestureEvent& gesture = + static_cast<const ui::GestureEvent&>(event); + event_bounds = gesture.details().bounding_box_f(); + } + + return event_bounds; +} + +ui::EventTarget* ViewTargeter::FindTargetForEvent(ui::EventTarget* root, + ui::Event* event) { + View* view = static_cast<View*>(root); + if (event->IsKeyEvent()) + return FindTargetForKeyEvent(view, *static_cast<ui::KeyEvent*>(event)); + else if (event->IsScrollEvent()) + return EventTargeter::FindTargetForEvent(root, event); + + NOTREACHED() << "ViewTargeter does not yet support this event type."; + return NULL; +} + +ui::EventTarget* ViewTargeter::FindNextBestTarget( + ui::EventTarget* previous_target, + ui::Event* event) { + return previous_target->GetParentTarget(); +} + +bool ViewTargeter::SubtreeCanAcceptEvent( + ui::EventTarget* target, + const ui::LocatedEvent& event) const { + views::View* view = static_cast<views::View*>(target); + if (!view->visible()) + return false; + + if (!view->CanProcessEventsWithinSubtree()) + return false; + + return true; +} + +bool ViewTargeter::EventLocationInsideBounds( + ui::EventTarget* target, + const ui::LocatedEvent& event) const { + views::View* view = static_cast<views::View*>(target); + gfx::Rect rect(event.location(), gfx::Size(1, 1)); + gfx::RectF rect_in_view_coords_f(rect); + if (view->parent()) + View::ConvertRectToTarget(view->parent(), view, &rect_in_view_coords_f); + gfx::Rect rect_in_view_coords = gfx::ToEnclosingRect(rect_in_view_coords_f); + + // TODO(tdanderson): Don't call into HitTestRect() directly here. + // See crbug.com/370579. + return view->HitTestRect(rect_in_view_coords); +} + +View* ViewTargeter::FindTargetForKeyEvent(View* view, const ui::KeyEvent& key) { + if (view->GetFocusManager()) + return view->GetFocusManager()->GetFocusedView(); + return NULL; +} + +} // namespace aura diff --git a/chromium/ui/views/view_targeter.h b/chromium/ui/views/view_targeter.h new file mode 100644 index 00000000000..8273a8aabc4 --- /dev/null +++ b/chromium/ui/views/view_targeter.h @@ -0,0 +1,51 @@ +// Copyright 2014 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_VIEW_TARGETER_H_ +#define UI_VIEWS_VIEW_TARGETER_H_ + +#include "ui/events/event_targeter.h" +#include "ui/views/views_export.h" + +namespace views { + +class View; + +// Contains the logic used to determine the View to which an +// event should be dispatched. A ViewTargeter (or one of its +// derived classes) is installed on a View to specify the +// targeting behaviour to be used for the subtree rooted at +// that View. +class VIEWS_EXPORT ViewTargeter : public ui::EventTargeter { + public: + ViewTargeter(); + virtual ~ViewTargeter(); + + protected: + // Returns the location of |event| represented as a rect. If |event| is + // a gesture event, its bounding box is returned. Otherwise, a 1x1 rect + // having its origin at the location of |event| is returned. + gfx::RectF BoundsForEvent(const ui::LocatedEvent& event) const; + + // ui::EventTargeter: + virtual ui::EventTarget* FindTargetForEvent(ui::EventTarget* root, + ui::Event* event) OVERRIDE; + virtual ui::EventTarget* FindNextBestTarget(ui::EventTarget* previous_target, + ui::Event* event) OVERRIDE; + virtual bool SubtreeCanAcceptEvent( + ui::EventTarget* target, + const ui::LocatedEvent& event) const OVERRIDE; + virtual bool EventLocationInsideBounds( + ui::EventTarget* target, + const ui::LocatedEvent& event) const OVERRIDE; + + private: + View* FindTargetForKeyEvent(View* view, const ui::KeyEvent& key); + + DISALLOW_COPY_AND_ASSIGN(ViewTargeter); +}; + +} // namespace views + +#endif // UI_VIEWS_VIEW_TARGETER_H_ diff --git a/chromium/ui/views/view_targeter_unittest.cc b/chromium/ui/views/view_targeter_unittest.cc new file mode 100644 index 00000000000..54a28b13a32 --- /dev/null +++ b/chromium/ui/views/view_targeter_unittest.cc @@ -0,0 +1,386 @@ +// Copyright 2014 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/view_targeter.h" + +#include "ui/events/event_targeter.h" +#include "ui/events/event_utils.h" +#include "ui/gfx/path.h" +#include "ui/views/masked_view_targeter.h" +#include "ui/views/test/views_test_base.h" +#include "ui/views/widget/root_view.h" + +namespace views { + +// A class used to define a triangular-shaped hit test mask on a View. +class TestMaskedViewTargeter : public MaskedViewTargeter { + public: + explicit TestMaskedViewTargeter(View* masked_view) + : MaskedViewTargeter(masked_view) {} + virtual ~TestMaskedViewTargeter() {} + + private: + virtual bool GetHitTestMask(const View* view, + gfx::Path* mask) const OVERRIDE { + SkScalar w = SkIntToScalar(view->width()); + SkScalar h = SkIntToScalar(view->height()); + + // Create a triangular mask within the bounds of |view|. + mask->moveTo(w / 2, 0); + mask->lineTo(w, h); + mask->lineTo(0, h); + mask->close(); + + return true; + } + + DISALLOW_COPY_AND_ASSIGN(TestMaskedViewTargeter); +}; + +// A derived class of View used for testing purposes. +class TestingView : public View { + public: + TestingView() : can_process_events_within_subtree_(true) {} + virtual ~TestingView() {} + + // Reset all test state. + void Reset() { can_process_events_within_subtree_ = true; } + + void set_can_process_events_within_subtree(bool can_process) { + can_process_events_within_subtree_ = can_process; + } + + // View: + virtual bool CanProcessEventsWithinSubtree() const OVERRIDE { + return can_process_events_within_subtree_; + } + + private: + // Value to return from CanProcessEventsWithinSubtree(). + bool can_process_events_within_subtree_; + + DISALLOW_COPY_AND_ASSIGN(TestingView); +}; + +namespace test { + +typedef ViewsTestBase ViewTargeterTest; + +// Verifies that the the functions ViewTargeter::FindTargetForEvent() +// and ViewTargeter::FindNextBestTarget() are implemented correctly +// for key events. +TEST_F(ViewTargeterTest, ViewTargeterForKeyEvents) { + Widget widget; + Widget::InitParams init_params = + CreateParams(Widget::InitParams::TYPE_POPUP); + init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget.Init(init_params); + + View* content = new View; + View* child = new View; + View* grandchild = new View; + + widget.SetContentsView(content); + content->AddChildView(child); + child->AddChildView(grandchild); + + grandchild->SetFocusable(true); + grandchild->RequestFocus(); + + ui::EventTargeter* targeter = new ViewTargeter(); + internal::RootView* root_view = + static_cast<internal::RootView*>(widget.GetRootView()); + root_view->SetEventTargeter(make_scoped_ptr(targeter)); + + ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_A, 0, true); + + // The focused view should be the initial target of the event. + ui::EventTarget* current_target = targeter->FindTargetForEvent(root_view, + &key_event); + EXPECT_EQ(grandchild, static_cast<View*>(current_target)); + + // Verify that FindNextBestTarget() will return the parent view of the + // argument (and NULL if the argument has no parent view). + current_target = targeter->FindNextBestTarget(grandchild, &key_event); + EXPECT_EQ(child, static_cast<View*>(current_target)); + current_target = targeter->FindNextBestTarget(child, &key_event); + EXPECT_EQ(content, static_cast<View*>(current_target)); + current_target = targeter->FindNextBestTarget(content, &key_event); + EXPECT_EQ(widget.GetRootView(), static_cast<View*>(current_target)); + current_target = targeter->FindNextBestTarget(widget.GetRootView(), + &key_event); + EXPECT_EQ(NULL, static_cast<View*>(current_target)); +} + +// Verifies that the the functions ViewTargeter::FindTargetForEvent() +// and ViewTargeter::FindNextBestTarget() are implemented correctly +// for scroll events. +TEST_F(ViewTargeterTest, ViewTargeterForScrollEvents) { + Widget widget; + Widget::InitParams init_params = + CreateParams(Widget::InitParams::TYPE_POPUP); + init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + init_params.bounds = gfx::Rect(0, 0, 200, 200); + widget.Init(init_params); + + // The coordinates used for SetBounds() are in the parent coordinate space. + View* content = new View; + content->SetBounds(0, 0, 100, 100); + View* child = new View; + child->SetBounds(50, 50, 20, 20); + View* grandchild = new View; + grandchild->SetBounds(0, 0, 5, 5); + + widget.SetContentsView(content); + content->AddChildView(child); + child->AddChildView(grandchild); + + ui::EventTargeter* targeter = new ViewTargeter(); + internal::RootView* root_view = + static_cast<internal::RootView*>(widget.GetRootView()); + root_view->SetEventTargeter(make_scoped_ptr(targeter)); + + // The event falls within the bounds of |child| and |content| but not + // |grandchild|, so |child| should be the initial target for the event. + ui::ScrollEvent scroll(ui::ET_SCROLL, + gfx::Point(60, 60), + ui::EventTimeForNow(), + 0, + 0, 3, + 0, 3, + 2); + ui::EventTarget* current_target = targeter->FindTargetForEvent(root_view, + &scroll); + EXPECT_EQ(child, static_cast<View*>(current_target)); + + // Verify that FindNextBestTarget() will return the parent view of the + // argument (and NULL if the argument has no parent view). + current_target = targeter->FindNextBestTarget(child, &scroll); + EXPECT_EQ(content, static_cast<View*>(current_target)); + current_target = targeter->FindNextBestTarget(content, &scroll); + EXPECT_EQ(widget.GetRootView(), static_cast<View*>(current_target)); + current_target = targeter->FindNextBestTarget(widget.GetRootView(), + &scroll); + EXPECT_EQ(NULL, static_cast<View*>(current_target)); + + // The event falls outside of the original specified bounds of |content|, + // |child|, and |grandchild|. But since |content| is the contents view, + // and contents views are resized to fill the entire area of the root + // view, the event's initial target should still be |content|. + scroll = ui::ScrollEvent(ui::ET_SCROLL, + gfx::Point(150, 150), + ui::EventTimeForNow(), + 0, + 0, 3, + 0, 3, + 2); + current_target = targeter->FindTargetForEvent(root_view, &scroll); + EXPECT_EQ(content, static_cast<View*>(current_target)); +} + +// Tests the basic functionality of the method +// ViewTargeter::SubtreeShouldBeExploredForEvent(). +TEST_F(ViewTargeterTest, SubtreeShouldBeExploredForEvent) { + Widget widget; + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(0, 0, 650, 650); + widget.Init(params); + + ui::EventTargeter* targeter = new ViewTargeter(); + internal::RootView* root_view = + static_cast<internal::RootView*>(widget.GetRootView()); + root_view->SetEventTargeter(make_scoped_ptr(targeter)); + + // The coordinates used for SetBounds() are in the parent coordinate space. + View v1, v2, v3; + v1.SetBounds(0, 0, 300, 300); + v2.SetBounds(100, 100, 100, 100); + v3.SetBounds(0, 0, 10, 10); + v3.SetVisible(false); + root_view->AddChildView(&v1); + v1.AddChildView(&v2); + v2.AddChildView(&v3); + + // Note that the coordinates used below are in |v1|'s coordinate space, + // and that SubtreeShouldBeExploredForEvent() expects the event location + // to be in the coordinate space of the target's parent. |v1| and + // its parent share a common coordinate space. + + // Event located within |v1| only. + gfx::Point point(10, 10); + ui::MouseEvent event(ui::ET_MOUSE_PRESSED, point, point, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + EXPECT_TRUE(targeter->SubtreeShouldBeExploredForEvent(&v1, event)); + EXPECT_FALSE(targeter->SubtreeShouldBeExploredForEvent(&v2, event)); + v1.ConvertEventToTarget(&v2, &event); + EXPECT_FALSE(targeter->SubtreeShouldBeExploredForEvent(&v3, event)); + + // Event located within |v1| and |v2| only. + event.set_location(gfx::Point(150, 150)); + EXPECT_TRUE(targeter->SubtreeShouldBeExploredForEvent(&v1, event)); + EXPECT_TRUE(targeter->SubtreeShouldBeExploredForEvent(&v2, event)); + v1.ConvertEventToTarget(&v2, &event); + EXPECT_FALSE(targeter->SubtreeShouldBeExploredForEvent(&v3, event)); + + // Event located within |v1|, |v2|, and |v3|. Note that |v3| is not + // visible, so it cannot handle the event. + event.set_location(gfx::Point(105, 105)); + EXPECT_TRUE(targeter->SubtreeShouldBeExploredForEvent(&v1, event)); + EXPECT_TRUE(targeter->SubtreeShouldBeExploredForEvent(&v2, event)); + v1.ConvertEventToTarget(&v2, &event); + EXPECT_FALSE(targeter->SubtreeShouldBeExploredForEvent(&v3, event)); + + // Event located outside the bounds of all views. + event.set_location(gfx::Point(400, 400)); + EXPECT_FALSE(targeter->SubtreeShouldBeExploredForEvent(&v1, event)); + EXPECT_FALSE(targeter->SubtreeShouldBeExploredForEvent(&v2, event)); + v1.ConvertEventToTarget(&v2, &event); + EXPECT_FALSE(targeter->SubtreeShouldBeExploredForEvent(&v3, event)); + + // TODO(tdanderson): Move the hit-testing unit tests out of view_unittest + // and into here. See crbug.com/355425. +} + +// Tests that FindTargetForEvent() returns the correct target when some +// views in the view tree return false when CanProcessEventsWithinSubtree() +// is called on them. +TEST_F(ViewTargeterTest, CanProcessEventsWithinSubtree) { + Widget widget; + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(0, 0, 650, 650); + widget.Init(params); + + ui::EventTargeter* targeter = new ViewTargeter(); + internal::RootView* root_view = + static_cast<internal::RootView*>(widget.GetRootView()); + root_view->SetEventTargeter(make_scoped_ptr(targeter)); + + // The coordinates used for SetBounds() are in the parent coordinate space. + TestingView v1, v2, v3; + v1.SetBounds(0, 0, 300, 300); + v2.SetBounds(100, 100, 100, 100); + v3.SetBounds(0, 0, 10, 10); + root_view->AddChildView(&v1); + v1.AddChildView(&v2); + v2.AddChildView(&v3); + + // Note that the coordinates used below are in the coordinate space of + // the root view. + + // Define |scroll| to be (105, 105) (in the coordinate space of the root + // view). This is located within all of |v1|, |v2|, and |v3|. + gfx::Point scroll_point(105, 105); + ui::ScrollEvent scroll( + ui::ET_SCROLL, scroll_point, ui::EventTimeForNow(), 0, 0, 3, 0, 3, 2); + + // If CanProcessEventsWithinSubtree() returns true for each view, + // |scroll| should be targeted at the deepest view in the hierarchy, + // which is |v3|. + ui::EventTarget* current_target = + targeter->FindTargetForEvent(root_view, &scroll); + EXPECT_EQ(&v3, current_target); + + // If CanProcessEventsWithinSubtree() returns |false| when called + // on |v3|, then |v3| cannot be the target of |scroll| (this should + // instead be |v2|). Note we need to reset the location of |scroll| + // because it may have been mutated by the previous call to + // FindTargetForEvent(). + scroll.set_location(scroll_point); + v3.set_can_process_events_within_subtree(false); + current_target = targeter->FindTargetForEvent(root_view, &scroll); + EXPECT_EQ(&v2, current_target); + + // If CanProcessEventsWithinSubtree() returns |false| when called + // on |v2|, then neither |v2| nor |v3| can be the target of |scroll| + // (this should instead be |v1|). + scroll.set_location(scroll_point); + v3.Reset(); + v2.set_can_process_events_within_subtree(false); + current_target = targeter->FindTargetForEvent(root_view, &scroll); + EXPECT_EQ(&v1, current_target); + + // If CanProcessEventsWithinSubtree() returns |false| when called + // on |v1|, then none of |v1|, |v2| or |v3| can be the target of |scroll| + // (this should instead be the root view itself). + scroll.set_location(scroll_point); + v2.Reset(); + v1.set_can_process_events_within_subtree(false); + current_target = targeter->FindTargetForEvent(root_view, &scroll); + EXPECT_EQ(root_view, current_target); + + // TODO(tdanderson): We should also test that targeting works correctly + // with gestures. See crbug.com/375822. +} + +// Tests that FindTargetForEvent() returns the correct target when some +// views in the view tree have a MaskedViewTargeter installed, i.e., +// they have a custom-shaped hit test mask. +TEST_F(ViewTargeterTest, MaskedViewTargeter) { + Widget widget; + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(0, 0, 650, 650); + widget.Init(params); + + ui::EventTargeter* targeter = new ViewTargeter(); + internal::RootView* root_view = + static_cast<internal::RootView*>(widget.GetRootView()); + root_view->SetEventTargeter(make_scoped_ptr(targeter)); + + // The coordinates used for SetBounds() are in the parent coordinate space. + View masked_view, unmasked_view, masked_child; + masked_view.SetBounds(0, 0, 200, 200); + unmasked_view.SetBounds(300, 0, 300, 300); + masked_child.SetBounds(0, 0, 100, 100); + root_view->AddChildView(&masked_view); + root_view->AddChildView(&unmasked_view); + unmasked_view.AddChildView(&masked_child); + + // Install event targeters of type TestMaskedViewTargeter on the two masked + // views to define their hit test masks. + ui::EventTargeter* masked_targeter = new TestMaskedViewTargeter(&masked_view); + masked_view.SetEventTargeter(make_scoped_ptr(masked_targeter)); + masked_targeter = new TestMaskedViewTargeter(&masked_child); + masked_child.SetEventTargeter(make_scoped_ptr(masked_targeter)); + + // Note that the coordinates used below are in the coordinate space of + // the root view. + + // Event located within the hit test mask of |masked_view|. + ui::ScrollEvent scroll(ui::ET_SCROLL, + gfx::Point(100, 190), + ui::EventTimeForNow(), + 0, + 0, + 3, + 0, + 3, + 2); + ui::EventTarget* current_target = + targeter->FindTargetForEvent(root_view, &scroll); + EXPECT_EQ(&masked_view, static_cast<View*>(current_target)); + + // Event located outside the hit test mask of |masked_view|. + scroll.set_location(gfx::Point(10, 10)); + current_target = targeter->FindTargetForEvent(root_view, &scroll); + EXPECT_EQ(root_view, static_cast<View*>(current_target)); + + // Event located within the hit test mask of |masked_child|. + scroll.set_location(gfx::Point(350, 3)); + current_target = targeter->FindTargetForEvent(root_view, &scroll); + EXPECT_EQ(&masked_child, static_cast<View*>(current_target)); + + // Event located within the hit test mask of |masked_child|. + scroll.set_location(gfx::Point(300, 12)); + current_target = targeter->FindTargetForEvent(root_view, &scroll); + EXPECT_EQ(&unmasked_view, static_cast<View*>(current_target)); + + // TODO(tdanderson): We should also test that targeting of masked views + // works correctly with gestures. See crbug.com/375822. +} + +} // namespace test +} // namespace views diff --git a/chromium/ui/views/view_unittest.cc b/chromium/ui/views/view_unittest.cc index 77601b20eef..a8639bcdd0d 100644 --- a/chromium/ui/views/view_unittest.cc +++ b/chromium/ui/views/view_unittest.cc @@ -9,14 +9,15 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "grit/ui_strings.h" -#include "testing/gmock/include/gmock/gmock.h" #include "ui/base/accelerators/accelerator.h" #include "ui/base/clipboard/clipboard.h" #include "ui/base/l10n/l10n_util.h" #include "ui/compositor/compositor.h" #include "ui/compositor/layer.h" #include "ui/compositor/layer_animator.h" +#include "ui/compositor/test/draw_waiter_for_test.h" #include "ui/events/event.h" +#include "ui/events/gestures/gesture_recognizer.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/canvas.h" #include "ui/gfx/path.h" @@ -25,7 +26,6 @@ #include "ui/views/controls/native/native_view_host.h" #include "ui/views/controls/scroll_view.h" #include "ui/views/controls/textfield/textfield.h" -#include "ui/views/focus/accelerator_handler.h" #include "ui/views/focus/view_storage.h" #include "ui/views/test/views_test_base.h" #include "ui/views/view.h" @@ -35,15 +35,7 @@ #include "ui/views/window/dialog_client_view.h" #include "ui/views/window/dialog_delegate.h" -#if defined(OS_WIN) -#include "ui/views/test/test_views_delegate.h" -#endif -#if defined(USE_AURA) -#include "ui/aura/root_window.h" -#include "ui/events/gestures/gesture_recognizer.h" -#endif - -using ::testing::_; +using base::ASCIIToUTF16; namespace { @@ -205,7 +197,11 @@ typedef ViewsTestBase ViewTest; // A derived class for testing purpose. class TestView : public View { public: - TestView() : View(), delete_on_pressed_(false), in_touch_sequence_(false) {} + TestView() + : View(), + delete_on_pressed_(false), + native_theme_(NULL), + can_process_events_within_subtree_(true) {} virtual ~TestView() {} // Reset all test state @@ -215,12 +211,11 @@ class TestView : public View { location_.SetPoint(0, 0); received_mouse_enter_ = false; received_mouse_exit_ = false; - last_touch_event_type_ = 0; - last_touch_event_was_handled_ = false; last_gesture_event_type_ = 0; last_gesture_event_was_handled_ = false; last_clip_.setEmpty(); accelerator_count_map_.clear(); + can_process_events_within_subtree_ = true; } // Exposed as public for testing. @@ -232,6 +227,16 @@ class TestView : public View { views::View::Blur(); } + bool focusable() const { return View::focusable(); } + + void set_can_process_events_within_subtree(bool can_process) { + can_process_events_within_subtree_ = can_process; + } + + virtual bool CanProcessEventsWithinSubtree() const OVERRIDE { + return can_process_events_within_subtree_; + } + virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE; virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE; virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE; @@ -239,14 +244,16 @@ class TestView : public View { virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE; virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE; - virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE; // Ignores GestureEvent by default. virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; - virtual void Paint(gfx::Canvas* canvas) OVERRIDE; + virtual void Paint(gfx::Canvas* canvas, const CullSet& cull_set) OVERRIDE; virtual void SchedulePaintInRect(const gfx::Rect& rect) OVERRIDE; virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE; + virtual void OnNativeThemeChanged(const ui::NativeTheme* native_theme) + OVERRIDE; + // OnBoundsChanged. bool did_change_bounds_; gfx::Rect new_bounds_; @@ -265,26 +272,17 @@ class TestView : public View { int last_gesture_event_type_; bool last_gesture_event_was_handled_; - // TouchEvent. - int last_touch_event_type_; - bool last_touch_event_was_handled_; - bool in_touch_sequence_; - // Painting. SkRect last_clip_; // Accelerators. std::map<ui::Accelerator, int> accelerator_count_map_; -}; -// A view subclass that ignores all touch events for testing purposes. -class TestViewIgnoreTouch : public TestView { - public: - TestViewIgnoreTouch() : TestView() {} - virtual ~TestViewIgnoreTouch() {} + // Native theme. + const ui::NativeTheme* native_theme_; - private: - virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE; + // Value to return from CanProcessEventsWithinSubtree(). + bool can_process_events_within_subtree_; }; // A view subclass that consumes all Gesture events for testing purposes. @@ -412,7 +410,7 @@ TEST_F(ViewTest, MouseEvent) { gfx::Point p1(110, 120); ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1, - ui::EF_LEFT_MOUSE_BUTTON); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); root->OnMousePressed(pressed); EXPECT_EQ(v2->last_mouse_event_type_, ui::ET_MOUSE_PRESSED); EXPECT_EQ(v2->location_.x(), 10); @@ -425,7 +423,7 @@ TEST_F(ViewTest, MouseEvent) { v2->Reset(); gfx::Point p2(50, 40); ui::MouseEvent dragged(ui::ET_MOUSE_DRAGGED, p2, p2, - ui::EF_LEFT_MOUSE_BUTTON); + ui::EF_LEFT_MOUSE_BUTTON, 0); root->OnMouseDragged(dragged); EXPECT_EQ(v2->last_mouse_event_type_, ui::ET_MOUSE_DRAGGED); EXPECT_EQ(v2->location_.x(), -50); @@ -436,7 +434,8 @@ TEST_F(ViewTest, MouseEvent) { // Releasted event out of bounds. Should still go to v2 v1->Reset(); v2->Reset(); - ui::MouseEvent released(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), 0); + ui::MouseEvent released(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), 0, + 0); root->OnMouseDragged(released); EXPECT_EQ(v2->last_mouse_event_type_, ui::ET_MOUSE_RELEASED); EXPECT_EQ(v2->location_.x(), -100); @@ -471,7 +470,7 @@ TEST_F(ViewTest, DeleteOnPressed) { v2->delete_on_pressed_ = true; gfx::Point point(110, 120); ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, point, point, - ui::EF_LEFT_MOUSE_BUTTON); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); root->OnMousePressed(pressed); EXPECT_EQ(0, v1->child_count()); @@ -479,129 +478,8 @@ TEST_F(ViewTest, DeleteOnPressed) { } //////////////////////////////////////////////////////////////////////////////// -// TouchEvent +// GestureEvent //////////////////////////////////////////////////////////////////////////////// -void TestView::OnTouchEvent(ui::TouchEvent* event) { - last_touch_event_type_ = event->type(); - location_.SetPoint(event->x(), event->y()); - if (!in_touch_sequence_) { - if (event->type() == ui::ET_TOUCH_PRESSED) { - in_touch_sequence_ = true; - event->StopPropagation(); - return; - } - } else { - if (event->type() == ui::ET_TOUCH_RELEASED) { - in_touch_sequence_ = false; - event->SetHandled(); - return; - } - event->StopPropagation(); - return; - } - - if (last_touch_event_was_handled_) - event->StopPropagation(); -} - -void TestViewIgnoreTouch::OnTouchEvent(ui::TouchEvent* event) { -} - -TEST_F(ViewTest, TouchEvent) { - TestView* v1 = new TestView(); - v1->SetBoundsRect(gfx::Rect(0, 0, 300, 300)); - - TestView* v2 = new TestView(); - v2->SetBoundsRect(gfx::Rect(100, 100, 100, 100)); - - TestView* v3 = new TestViewIgnoreTouch(); - v3->SetBoundsRect(gfx::Rect(0, 0, 100, 100)); - - scoped_ptr<Widget> widget(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); - widget->Init(params); - internal::RootView* root = - static_cast<internal::RootView*>(widget->GetRootView()); - - root->AddChildView(v1); - v1->AddChildView(v2); - v2->AddChildView(v3); - - // |v3| completely obscures |v2|, but all the touch events on |v3| should - // reach |v2| because |v3| doesn't process any touch events. - - // Make sure if none of the views handle the touch event, the gesture manager - // does. - v1->Reset(); - v2->Reset(); - - ui::TouchEvent unhandled(ui::ET_TOUCH_MOVED, - gfx::Point(400, 400), - 0, /* no flags */ - 0, /* first finger touch */ - base::TimeDelta(), - 1.0, 0.0, 1.0, 0.0); - root->DispatchTouchEvent(&unhandled); - - EXPECT_EQ(v1->last_touch_event_type_, 0); - EXPECT_EQ(v2->last_touch_event_type_, 0); - - // Test press, drag, release touch sequence. - v1->Reset(); - v2->Reset(); - - ui::TouchEvent pressed(ui::ET_TOUCH_PRESSED, - gfx::Point(110, 120), - 0, /* no flags */ - 0, /* first finger touch */ - base::TimeDelta(), - 1.0, 0.0, 1.0, 0.0); - v2->last_touch_event_was_handled_ = true; - root->DispatchTouchEvent(&pressed); - - EXPECT_EQ(v2->last_touch_event_type_, ui::ET_TOUCH_PRESSED); - EXPECT_EQ(v2->location_.x(), 10); - EXPECT_EQ(v2->location_.y(), 20); - // Make sure v1 did not receive the event - EXPECT_EQ(v1->last_touch_event_type_, 0); - - // Drag event out of bounds. Should still go to v2 - v1->Reset(); - v2->Reset(); - ui::TouchEvent dragged(ui::ET_TOUCH_MOVED, - gfx::Point(50, 40), - 0, /* no flags */ - 0, /* first finger touch */ - base::TimeDelta(), - 1.0, 0.0, 1.0, 0.0); - - root->DispatchTouchEvent(&dragged); - EXPECT_EQ(v2->last_touch_event_type_, ui::ET_TOUCH_MOVED); - EXPECT_EQ(v2->location_.x(), -50); - EXPECT_EQ(v2->location_.y(), -60); - // Make sure v1 did not receive the event - EXPECT_EQ(v1->last_touch_event_type_, 0); - - // Released event out of bounds. Should still go to v2 - v1->Reset(); - v2->Reset(); - ui::TouchEvent released(ui::ET_TOUCH_RELEASED, gfx::Point(), - 0, /* no flags */ - 0, /* first finger */ - base::TimeDelta(), - 1.0, 0.0, 1.0, 0.0); - v2->last_touch_event_was_handled_ = true; - root->DispatchTouchEvent(&released); - EXPECT_EQ(v2->last_touch_event_type_, ui::ET_TOUCH_RELEASED); - EXPECT_EQ(v2->location_.x(), -100); - EXPECT_EQ(v2->location_.y(), -100); - // Make sure v1 did not receive the event - EXPECT_EQ(v1->last_touch_event_type_, 0); - - widget->CloseNow(); -} void TestView::OnGestureEvent(ui::GestureEvent* event) { } @@ -624,6 +502,7 @@ TEST_F(ViewTest, GestureEvent) { widget->Init(params); internal::RootView* root = static_cast<internal::RootView*>(widget->GetRootView()); + ui::EventDispatchDetails details; root->AddChildView(v1); v1->AddChildView(v2); @@ -640,14 +519,19 @@ TEST_F(ViewTest, GestureEvent) { // Gesture on |v3| GestureEventForTest g1(ui::ET_GESTURE_TAP, 110, 110, 0); - root->DispatchGestureEvent(&g1); + details = root->OnEventFromSource(&g1); + EXPECT_FALSE(details.dispatcher_destroyed); + EXPECT_FALSE(details.target_destroyed); + EXPECT_EQ(ui::ET_GESTURE_TAP, v2->last_gesture_event_type_); EXPECT_EQ(gfx::Point(10, 10), v2->location_); EXPECT_EQ(ui::ET_UNKNOWN, v1->last_gesture_event_type_); // Simulate an up so that RootView is no longer targetting |v3|. GestureEventForTest g1_up(ui::ET_GESTURE_END, 110, 110, 0); - root->DispatchGestureEvent(&g1_up); + details = root->OnEventFromSource(&g1_up); + EXPECT_FALSE(details.dispatcher_destroyed); + EXPECT_FALSE(details.target_destroyed); v1->Reset(); v2->Reset(); @@ -655,7 +539,10 @@ TEST_F(ViewTest, GestureEvent) { // Gesture on |v1| GestureEventForTest g2(ui::ET_GESTURE_TAP, 80, 80, 0); - root->DispatchGestureEvent(&g2); + details = root->OnEventFromSource(&g2); + EXPECT_FALSE(details.dispatcher_destroyed); + EXPECT_FALSE(details.target_destroyed); + EXPECT_EQ(ui::ET_GESTURE_TAP, v1->last_gesture_event_type_); EXPECT_EQ(gfx::Point(80, 80), v1->location_); EXPECT_EQ(ui::ET_UNKNOWN, v2->last_gesture_event_type_); @@ -664,7 +551,10 @@ TEST_F(ViewTest, GestureEvent) { // to |v1| as that is the view the touch was initially down on. v1->last_gesture_event_type_ = ui::ET_UNKNOWN; v3->last_gesture_event_type_ = ui::ET_UNKNOWN; - root->DispatchGestureEvent(&g1); + details = root->OnEventFromSource(&g1); + EXPECT_FALSE(details.dispatcher_destroyed); + EXPECT_FALSE(details.target_destroyed); + EXPECT_EQ(ui::ET_GESTURE_TAP, v1->last_gesture_event_type_); EXPECT_EQ(ui::ET_UNKNOWN, v3->last_gesture_event_type_); EXPECT_EQ("110,110", v1->location_.ToString()); @@ -690,6 +580,7 @@ TEST_F(ViewTest, ScrollGestureEvent) { widget->Init(params); internal::RootView* root = static_cast<internal::RootView*>(widget->GetRootView()); + ui::EventDispatchDetails details; root->AddChildView(v1); v1->AddChildView(v2); @@ -706,7 +597,10 @@ TEST_F(ViewTest, ScrollGestureEvent) { // Gesture on |v3| GestureEventForTest g1(ui::ET_GESTURE_TAP, 110, 110, 0); - root->DispatchGestureEvent(&g1); + details = root->OnEventFromSource(&g1); + EXPECT_FALSE(details.dispatcher_destroyed); + EXPECT_FALSE(details.target_destroyed); + EXPECT_EQ(ui::ET_GESTURE_TAP, v2->last_gesture_event_type_); EXPECT_EQ(gfx::Point(10, 10), v2->location_); EXPECT_EQ(ui::ET_UNKNOWN, v1->last_gesture_event_type_); @@ -717,7 +611,10 @@ TEST_F(ViewTest, ScrollGestureEvent) { // since it does not process scroll-gesture events, these events should reach // |v1|. GestureEventForTest gscroll_begin(ui::ET_GESTURE_SCROLL_BEGIN, 115, 115, 0); - root->DispatchGestureEvent(&gscroll_begin); + details = root->OnEventFromSource(&gscroll_begin); + EXPECT_FALSE(details.dispatcher_destroyed); + EXPECT_FALSE(details.target_destroyed); + EXPECT_EQ(ui::ET_UNKNOWN, v2->last_gesture_event_type_); EXPECT_EQ(ui::ET_GESTURE_SCROLL_BEGIN, v1->last_gesture_event_type_); v1->Reset(); @@ -726,19 +623,28 @@ TEST_F(ViewTest, ScrollGestureEvent) { // default gesture handler, and not |v1| (even though it is the view under the // point, and is the scroll event handler). GestureEventForTest second_tap(ui::ET_GESTURE_TAP, 70, 70, 0); - root->DispatchGestureEvent(&second_tap); + details = root->OnEventFromSource(&second_tap); + EXPECT_FALSE(details.dispatcher_destroyed); + EXPECT_FALSE(details.target_destroyed); + EXPECT_EQ(ui::ET_GESTURE_TAP, v2->last_gesture_event_type_); EXPECT_EQ(ui::ET_UNKNOWN, v1->last_gesture_event_type_); v2->Reset(); GestureEventForTest gscroll_end(ui::ET_GESTURE_SCROLL_END, 50, 50, 0); - root->DispatchGestureEvent(&gscroll_end); + details = root->OnEventFromSource(&gscroll_end); + EXPECT_FALSE(details.dispatcher_destroyed); + EXPECT_FALSE(details.target_destroyed); + EXPECT_EQ(ui::ET_GESTURE_SCROLL_END, v1->last_gesture_event_type_); v1->Reset(); // Simulate an up so that RootView is no longer targetting |v3|. GestureEventForTest g1_up(ui::ET_GESTURE_END, 110, 110, 0); - root->DispatchGestureEvent(&g1_up); + details = root->OnEventFromSource(&g1_up); + EXPECT_FALSE(details.dispatcher_destroyed); + EXPECT_FALSE(details.target_destroyed); + EXPECT_EQ(ui::ET_GESTURE_END, v2->last_gesture_event_type_); v1->Reset(); @@ -747,7 +653,10 @@ TEST_F(ViewTest, ScrollGestureEvent) { // Gesture on |v1| GestureEventForTest g2(ui::ET_GESTURE_TAP, 80, 80, 0); - root->DispatchGestureEvent(&g2); + details = root->OnEventFromSource(&g2); + EXPECT_FALSE(details.dispatcher_destroyed); + EXPECT_FALSE(details.target_destroyed); + EXPECT_EQ(ui::ET_GESTURE_TAP, v1->last_gesture_event_type_); EXPECT_EQ(gfx::Point(80, 80), v1->location_); EXPECT_EQ(ui::ET_UNKNOWN, v2->last_gesture_event_type_); @@ -756,7 +665,10 @@ TEST_F(ViewTest, ScrollGestureEvent) { // to |v1| as that is the view the touch was initially down on. v1->last_gesture_event_type_ = ui::ET_UNKNOWN; v3->last_gesture_event_type_ = ui::ET_UNKNOWN; - root->DispatchGestureEvent(&g1); + details = root->OnEventFromSource(&g1); + EXPECT_FALSE(details.dispatcher_destroyed); + EXPECT_FALSE(details.target_destroyed); + EXPECT_EQ(ui::ET_GESTURE_TAP, v1->last_gesture_event_type_); EXPECT_EQ(ui::ET_UNKNOWN, v3->last_gesture_event_type_); EXPECT_EQ("110,110", v1->location_.ToString()); @@ -768,7 +680,7 @@ TEST_F(ViewTest, ScrollGestureEvent) { // Painting //////////////////////////////////////////////////////////////////////////////// -void TestView::Paint(gfx::Canvas* canvas) { +void TestView::Paint(gfx::Canvas* canvas, const CullSet& cull_set) { canvas->sk_canvas()->getClipBounds(&last_clip_); } @@ -784,66 +696,6 @@ void CheckRect(const SkRect& check_rect, const SkRect& target_rect) { EXPECT_EQ(target_rect.fBottom, check_rect.fBottom); } -/* This test is disabled because it is flakey on some systems. -TEST_F(ViewTest, DISABLED_Painting) { - // Determine if InvalidateRect generates an empty paint rectangle. - EmptyWindow paint_window(CRect(50, 50, 650, 650)); - paint_window.RedrawWindow(CRect(0, 0, 600, 600), NULL, - RDW_UPDATENOW | RDW_INVALIDATE | RDW_ALLCHILDREN); - bool empty_paint = paint_window.empty_paint(); - - NativeWidgetWin window; - window.set_delete_on_destroy(false); - window.set_window_style(WS_OVERLAPPEDWINDOW); - window.Init(NULL, gfx::Rect(50, 50, 650, 650), NULL); - View* root = window.GetRootView(); - - TestView* v1 = new TestView(); - v1->SetBoundsRect(gfx::Rect(0, 0, 650, 650)); - root->AddChildView(v1); - - TestView* v2 = new TestView(); - v2->SetBoundsRect(gfx::Rect(10, 10, 80, 80)); - v1->AddChildView(v2); - - TestView* v3 = new TestView(); - v3->SetBoundsRect(gfx::Rect(10, 10, 60, 60)); - v2->AddChildView(v3); - - TestView* v4 = new TestView(); - v4->SetBoundsRect(gfx::Rect(10, 200, 100, 100)); - v1->AddChildView(v4); - - // Make sure to paint current rects - PaintRootView(root, empty_paint); - - - v1->Reset(); - v2->Reset(); - v3->Reset(); - v4->Reset(); - v3->SchedulePaintInRect(gfx::Rect(10, 10, 10, 10)); - PaintRootView(root, empty_paint); - - SkRect tmp_rect; - - tmp_rect.iset(10, 10, 20, 20); - CheckRect(v3->last_clip_, tmp_rect); - - tmp_rect.iset(20, 20, 30, 30); - CheckRect(v2->last_clip_, tmp_rect); - - tmp_rect.iset(30, 30, 40, 40); - CheckRect(v1->last_clip_, tmp_rect); - - // Make sure v4 was not painted - tmp_rect.setEmpty(); - CheckRect(v4->last_clip_, tmp_rect); - - window.DestroyWindow(); -} -*/ - TEST_F(ViewTest, RemoveNotification) { ViewStorage* vs = ViewStorage::GetInstance(); Widget* widget = new Widget; @@ -1370,6 +1222,163 @@ TEST_F(ViewTest, GetEventHandlerForRect) { widget->CloseNow(); } +// Tests that GetEventHandlerForRect() and GetTooltipHandlerForPoint() behave +// as expected when different views in the view hierarchy return false +// when CanProcessEventsWithinSubtree() is called. +TEST_F(ViewTest, CanProcessEventsWithinSubtree) { + Widget* widget = new Widget; + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + widget->Init(params); + View* root_view = widget->GetRootView(); + root_view->SetBoundsRect(gfx::Rect(0, 0, 500, 500)); + + // Have this hierarchy of views (the coords here are in the coordinate + // space of the root view): + // v (0, 0, 100, 100) + // - v_child (0, 0, 20, 30) + // - v_grandchild (5, 5, 5, 15) + + TestView* v = new TestView; + v->SetBounds(0, 0, 100, 100); + root_view->AddChildView(v); + v->set_notify_enter_exit_on_child(true); + + TestView* v_child = new TestView; + v_child->SetBounds(0, 0, 20, 30); + v->AddChildView(v_child); + + TestView* v_grandchild = new TestView; + v_grandchild->SetBounds(5, 5, 5, 15); + v_child->AddChildView(v_grandchild); + + v->Reset(); + v_child->Reset(); + v_grandchild->Reset(); + + // Define rects and points within the views in the hierarchy. + gfx::Rect rect_in_v_grandchild(7, 7, 3, 3); + gfx::Point point_in_v_grandchild(rect_in_v_grandchild.origin()); + gfx::Rect rect_in_v_child(12, 3, 5, 5); + gfx::Point point_in_v_child(rect_in_v_child.origin()); + gfx::Rect rect_in_v(50, 50, 25, 30); + gfx::Point point_in_v(rect_in_v.origin()); + + // When all three views return true when CanProcessEventsWithinSubtree() + // is called, targeting should behave as expected. + + View* result_view = root_view->GetEventHandlerForRect(rect_in_v_grandchild); + EXPECT_EQ(v_grandchild, result_view); + result_view = NULL; + result_view = root_view->GetTooltipHandlerForPoint(point_in_v_grandchild); + EXPECT_EQ(v_grandchild, result_view); + result_view = NULL; + + result_view = root_view->GetEventHandlerForRect(rect_in_v_child); + EXPECT_EQ(v_child, result_view); + result_view = NULL; + result_view = root_view->GetTooltipHandlerForPoint(point_in_v_child); + EXPECT_EQ(v_child, result_view); + result_view = NULL; + + result_view = root_view->GetEventHandlerForRect(rect_in_v); + EXPECT_EQ(v, result_view); + result_view = NULL; + result_view = root_view->GetTooltipHandlerForPoint(point_in_v); + EXPECT_EQ(v, result_view); + result_view = NULL; + + // When |v_grandchild| returns false when CanProcessEventsWithinSubtree() + // is called, then |v_grandchild| cannot be returned as a target. + + v_grandchild->set_can_process_events_within_subtree(false); + + result_view = root_view->GetEventHandlerForRect(rect_in_v_grandchild); + EXPECT_EQ(v_child, result_view); + result_view = NULL; + result_view = root_view->GetTooltipHandlerForPoint(point_in_v_grandchild); + EXPECT_EQ(v_child, result_view); + result_view = NULL; + + result_view = root_view->GetEventHandlerForRect(rect_in_v_child); + EXPECT_EQ(v_child, result_view); + result_view = NULL; + result_view = root_view->GetTooltipHandlerForPoint(point_in_v_child); + EXPECT_EQ(v_child, result_view); + result_view = NULL; + + result_view = root_view->GetEventHandlerForRect(rect_in_v); + EXPECT_EQ(v, result_view); + result_view = NULL; + result_view = root_view->GetTooltipHandlerForPoint(point_in_v); + EXPECT_EQ(v, result_view); + + // When |v_grandchild| returns false when CanProcessEventsWithinSubtree() + // is called, then NULL should be returned as a target if we call + // GetTooltipHandlerForPoint() with |v_grandchild| as the root of the + // views tree. Note that the location must be in the coordinate space + // of the root view (|v_grandchild| in this case), so use (1, 1). + + result_view = v_grandchild; + result_view = v_grandchild->GetTooltipHandlerForPoint(gfx::Point(1, 1)); + EXPECT_EQ(NULL, result_view); + result_view = NULL; + + // When |v_child| returns false when CanProcessEventsWithinSubtree() + // is called, then neither |v_child| nor |v_grandchild| can be returned + // as a target (|v| should be returned as the target for each case). + + v_grandchild->Reset(); + v_child->set_can_process_events_within_subtree(false); + + result_view = root_view->GetEventHandlerForRect(rect_in_v_grandchild); + EXPECT_EQ(v, result_view); + result_view = NULL; + result_view = root_view->GetTooltipHandlerForPoint(point_in_v_grandchild); + EXPECT_EQ(v, result_view); + result_view = NULL; + + result_view = root_view->GetEventHandlerForRect(rect_in_v_child); + EXPECT_EQ(v, result_view); + result_view = NULL; + result_view = root_view->GetTooltipHandlerForPoint(point_in_v_child); + EXPECT_EQ(v, result_view); + result_view = NULL; + + result_view = root_view->GetEventHandlerForRect(rect_in_v); + EXPECT_EQ(v, result_view); + result_view = NULL; + result_view = root_view->GetTooltipHandlerForPoint(point_in_v); + EXPECT_EQ(v, result_view); + result_view = NULL; + + // When |v| returns false when CanProcessEventsWithinSubtree() + // is called, then none of |v|, |v_child|, and |v_grandchild| can be returned + // as a target (|root_view| should be returned as the target for each case). + + v_child->Reset(); + v->set_can_process_events_within_subtree(false); + + result_view = root_view->GetEventHandlerForRect(rect_in_v_grandchild); + EXPECT_EQ(root_view, result_view); + result_view = NULL; + result_view = root_view->GetTooltipHandlerForPoint(point_in_v_grandchild); + EXPECT_EQ(root_view, result_view); + result_view = NULL; + + result_view = root_view->GetEventHandlerForRect(rect_in_v_child); + EXPECT_EQ(root_view, result_view); + result_view = NULL; + result_view = root_view->GetTooltipHandlerForPoint(point_in_v_child); + EXPECT_EQ(root_view, result_view); + result_view = NULL; + + result_view = root_view->GetEventHandlerForRect(rect_in_v); + EXPECT_EQ(root_view, result_view); + result_view = NULL; + result_view = root_view->GetTooltipHandlerForPoint(point_in_v); + EXPECT_EQ(root_view, result_view); +} + TEST_F(ViewTest, NotifyEnterExitOnChild) { Widget* widget = new Widget; Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); @@ -1425,7 +1434,7 @@ TEST_F(ViewTest, NotifyEnterExitOnChild) { // Move the mouse in v111. gfx::Point p1(6, 6); - ui::MouseEvent move1(ui::ET_MOUSE_MOVED, p1, p1, 0); + ui::MouseEvent move1(ui::ET_MOUSE_MOVED, p1, p1, 0, 0); root_view->OnMouseMoved(move1); EXPECT_TRUE(v111->received_mouse_enter_); EXPECT_FALSE(v11->last_mouse_event_type_); @@ -1436,7 +1445,7 @@ TEST_F(ViewTest, NotifyEnterExitOnChild) { // Now, move into v121. gfx::Point p2(65, 21); - ui::MouseEvent move2(ui::ET_MOUSE_MOVED, p2, p2, 0); + ui::MouseEvent move2(ui::ET_MOUSE_MOVED, p2, p2, 0, 0); root_view->OnMouseMoved(move2); EXPECT_TRUE(v111->received_mouse_exit_); EXPECT_TRUE(v121->received_mouse_enter_); @@ -1447,7 +1456,7 @@ TEST_F(ViewTest, NotifyEnterExitOnChild) { // Now, move into v11. gfx::Point p3(1, 1); - ui::MouseEvent move3(ui::ET_MOUSE_MOVED, p3, p3, 0); + ui::MouseEvent move3(ui::ET_MOUSE_MOVED, p3, p3, 0, 0); root_view->OnMouseMoved(move3); EXPECT_TRUE(v121->received_mouse_exit_); EXPECT_TRUE(v11->received_mouse_enter_); @@ -1458,7 +1467,7 @@ TEST_F(ViewTest, NotifyEnterExitOnChild) { // Move to v21. gfx::Point p4(121, 15); - ui::MouseEvent move4(ui::ET_MOUSE_MOVED, p4, p4, 0); + ui::MouseEvent move4(ui::ET_MOUSE_MOVED, p4, p4, 0, 0); root_view->OnMouseMoved(move4); EXPECT_TRUE(v21->received_mouse_enter_); EXPECT_FALSE(v2->last_mouse_event_type_); @@ -1471,7 +1480,7 @@ TEST_F(ViewTest, NotifyEnterExitOnChild) { // Move to v1. gfx::Point p5(21, 0); - ui::MouseEvent move5(ui::ET_MOUSE_MOVED, p5, p5, 0); + ui::MouseEvent move5(ui::ET_MOUSE_MOVED, p5, p5, 0, 0); root_view->OnMouseMoved(move5); EXPECT_TRUE(v21->received_mouse_exit_); EXPECT_TRUE(v1->received_mouse_enter_); @@ -1481,7 +1490,7 @@ TEST_F(ViewTest, NotifyEnterExitOnChild) { // Now, move into v11. gfx::Point p6(15, 15); - ui::MouseEvent mouse6(ui::ET_MOUSE_MOVED, p6, p6, 0); + ui::MouseEvent mouse6(ui::ET_MOUSE_MOVED, p6, p6, 0, 0); root_view->OnMouseMoved(mouse6); EXPECT_TRUE(v11->received_mouse_enter_); EXPECT_FALSE(v1->last_mouse_event_type_); @@ -1493,7 +1502,7 @@ TEST_F(ViewTest, NotifyEnterExitOnChild) { // and the mouse remains inside |v1| the whole time, it receives another ENTER // when the mouse leaves v11. gfx::Point p7(21, 0); - ui::MouseEvent mouse7(ui::ET_MOUSE_MOVED, p7, p7, 0); + ui::MouseEvent mouse7(ui::ET_MOUSE_MOVED, p7, p7, 0, 0); root_view->OnMouseMoved(mouse7); EXPECT_TRUE(v11->received_mouse_exit_); EXPECT_FALSE(v1->received_mouse_enter_); @@ -1502,10 +1511,10 @@ TEST_F(ViewTest, NotifyEnterExitOnChild) { } TEST_F(ViewTest, Textfield) { - const string16 kText = ASCIIToUTF16("Reality is that which, when you stop " - "believing it, doesn't go away."); - const string16 kExtraText = ASCIIToUTF16("Pretty deep, Philip!"); - const string16 kEmptyString; + const base::string16 kText = ASCIIToUTF16( + "Reality is that which, when you stop believing it, doesn't go away."); + const base::string16 kExtraText = ASCIIToUTF16("Pretty deep, Philip!"); + const base::string16 kEmptyString; Widget* widget = new Widget; Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); @@ -1521,7 +1530,7 @@ TEST_F(ViewTest, Textfield) { EXPECT_EQ(kText, textfield->text()); textfield->AppendText(kExtraText); EXPECT_EQ(kText + kExtraText, textfield->text()); - textfield->SetText(string16()); + textfield->SetText(base::string16()); EXPECT_EQ(kEmptyString, textfield->text()); // Test selection related methods. @@ -1537,9 +1546,10 @@ TEST_F(ViewTest, Textfield) { // Tests that the Textfield view respond appropiately to cut/copy/paste. TEST_F(ViewTest, TextfieldCutCopyPaste) { - const string16 kNormalText = ASCIIToUTF16("Normal"); - const string16 kReadOnlyText = ASCIIToUTF16("Read only"); - const string16 kPasswordText = ASCIIToUTF16("Password! ** Secret stuff **"); + const base::string16 kNormalText = ASCIIToUTF16("Normal"); + const base::string16 kReadOnlyText = ASCIIToUTF16("Read only"); + const base::string16 kPasswordText = + ASCIIToUTF16("Password! ** Secret stuff **"); ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); @@ -1552,7 +1562,8 @@ TEST_F(ViewTest, TextfieldCutCopyPaste) { Textfield* normal = new Textfield(); Textfield* read_only = new Textfield(); read_only->SetReadOnly(true); - Textfield* password = new Textfield(Textfield::STYLE_OBSCURED); + Textfield* password = new Textfield(); + password->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); root_view->AddChildView(normal); root_view->AddChildView(read_only); @@ -1568,7 +1579,7 @@ TEST_F(ViewTest, TextfieldCutCopyPaste) { normal->SelectAll(false); normal->ExecuteCommand(IDS_APP_CUT); - string16 result; + base::string16 result; clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &result); EXPECT_EQ(kNormalText, result); normal->SetText(kNormalText); // Let's revert to the original content. @@ -1641,7 +1652,9 @@ bool TestView::AcceleratorPressed(const ui::Accelerator& accelerator) { return true; } -#if defined(OS_WIN) && !defined(USE_AURA) +// TODO: these tests were initially commented out when getting aura to +// run. Figure out if still valuable and either nuke or fix. +#if 0 TEST_F(ViewTest, ActivateAccelerator) { // Register a keyboard accelerator before the view is added to a window. ui::Accelerator return_accelerator(ui::VKEY_RETURN, ui::EF_NONE); @@ -1706,9 +1719,7 @@ TEST_F(ViewTest, ActivateAccelerator) { widget->CloseNow(); } -#endif -#if defined(OS_WIN) && !defined(USE_AURA) TEST_F(ViewTest, HiddenViewWithAccelerator) { ui::Accelerator return_accelerator(ui::VKEY_RETURN, ui::EF_NONE); TestView* view = new TestView(); @@ -1736,9 +1747,7 @@ TEST_F(ViewTest, HiddenViewWithAccelerator) { widget->CloseNow(); } -#endif -#if defined(OS_WIN) && !defined(USE_AURA) TEST_F(ViewTest, ViewInHiddenWidgetWithAccelerator) { ui::Accelerator return_accelerator(ui::VKEY_RETURN, ui::EF_NONE); TestView* view = new TestView(); @@ -1770,9 +1779,7 @@ TEST_F(ViewTest, ViewInHiddenWidgetWithAccelerator) { widget->CloseNow(); } -#endif -#if defined(OS_WIN) && !defined(USE_AURA) //////////////////////////////////////////////////////////////////////////////// // Mouse-wheel message rerouting //////////////////////////////////////////////////////////////////////////////// @@ -1849,20 +1856,10 @@ TEST_F(ViewTest, DISABLED_RerouteMouseWheelTest) { WM_MOUSEWHEEL, MAKEWPARAM(0, -20), MAKELPARAM(250, 250)); EXPECT_EQ(20, scroll_view->GetVisibleRect().y()); - // Then the text-field. - ::SendMessage(view_with_controls->text_field_->GetTestingHandle(), - WM_MOUSEWHEEL, MAKEWPARAM(0, -20), MAKELPARAM(250, 250)); - EXPECT_EQ(80, scroll_view->GetVisibleRect().y()); - - // Ensure we don't scroll when the mouse is not over that window. - ::SendMessage(view_with_controls->text_field_->GetTestingHandle(), - WM_MOUSEWHEEL, MAKEWPARAM(0, -20), MAKELPARAM(50, 50)); - EXPECT_EQ(80, scroll_view->GetVisibleRect().y()); - window1->CloseNow(); window2->CloseNow(); } -#endif +#endif // 0 //////////////////////////////////////////////////////////////////////////////// // Native view hierachy @@ -2030,14 +2027,15 @@ TEST_F(ViewTest, TransformEvent) { gfx::Point p1(110, 210); ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1, - ui::EF_LEFT_MOUSE_BUTTON); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); root->OnMousePressed(pressed); EXPECT_EQ(0, v1->last_mouse_event_type_); EXPECT_EQ(ui::ET_MOUSE_PRESSED, v2->last_mouse_event_type_); EXPECT_EQ(190, v2->location_.x()); EXPECT_EQ(10, v2->location_.y()); - ui::MouseEvent released(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), 0); + ui::MouseEvent released(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), 0, + 0); root->OnMouseReleased(released); // Now rotate |v2| inside |v1| clockwise. @@ -2054,7 +2052,7 @@ TEST_F(ViewTest, TransformEvent) { gfx::Point point2(110, 320); ui::MouseEvent p2(ui::ET_MOUSE_PRESSED, point2, point2, - ui::EF_LEFT_MOUSE_BUTTON); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); root->OnMousePressed(p2); EXPECT_EQ(0, v1->last_mouse_event_type_); EXPECT_EQ(ui::ET_MOUSE_PRESSED, v2->last_mouse_event_type_); @@ -2090,7 +2088,7 @@ TEST_F(ViewTest, TransformEvent) { gfx::Point point(112, 110); ui::MouseEvent p3(ui::ET_MOUSE_PRESSED, point, point, - ui::EF_LEFT_MOUSE_BUTTON); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); root->OnMousePressed(p3); EXPECT_EQ(ui::ET_MOUSE_PRESSED, v3->last_mouse_event_type_); @@ -2129,7 +2127,7 @@ TEST_F(ViewTest, TransformEvent) { gfx::Point point3(124, 125); ui::MouseEvent p4(ui::ET_MOUSE_PRESSED, point3, point3, - ui::EF_LEFT_MOUSE_BUTTON); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); root->OnMousePressed(p4); EXPECT_EQ(ui::ET_MOUSE_PRESSED, v3->last_mouse_event_type_); @@ -2299,6 +2297,39 @@ TEST_F(ViewTest, SetBoundsSameBoundsDoesntSchedulePaint) { EXPECT_TRUE(view.scheduled_paint_rects_.empty()); } +// Verifies AddChildView() and RemoveChildView() schedule appropriate paints. +TEST_F(ViewTest, AddAndRemoveSchedulePaints) { + gfx::Rect viewport_bounds(0, 0, 100, 100); + + // We have to put the View hierarchy into a Widget or no paints will be + // scheduled. + scoped_ptr<Widget> widget(new Widget); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = viewport_bounds; + widget->Init(params); + widget->GetRootView()->SetBoundsRect(viewport_bounds); + + TestView* parent_view = new TestView; + widget->SetContentsView(parent_view); + parent_view->SetBoundsRect(viewport_bounds); + parent_view->scheduled_paint_rects_.clear(); + + View* child_view = new View; + child_view->SetBoundsRect(gfx::Rect(0, 0, 20, 20)); + parent_view->AddChildView(child_view); + ASSERT_EQ(1U, parent_view->scheduled_paint_rects_.size()); + EXPECT_EQ(child_view->bounds(), parent_view->scheduled_paint_rects_.front()); + + parent_view->scheduled_paint_rects_.clear(); + parent_view->RemoveChildView(child_view); + scoped_ptr<View> child_deleter(child_view); + ASSERT_EQ(1U, parent_view->scheduled_paint_rects_.size()); + EXPECT_EQ(child_view->bounds(), parent_view->scheduled_paint_rects_.front()); + + widget->CloseNow(); +} + // Tests conversion methods with a transform. TEST_F(ViewTest, ConversionsWithTransform) { TestView top_view; @@ -2992,8 +3023,6 @@ TEST_F(ViewTest, AddExistingChild) { // Layers //////////////////////////////////////////////////////////////////////////////// -#if defined(USE_AURA) - namespace { // Test implementation of LayerAnimator. @@ -3027,7 +3056,7 @@ void TestLayerAnimator::SetBounds(const gfx::Rect& bounds) { class ViewLayerTest : public ViewsTestBase { public: - ViewLayerTest() : widget_(NULL), old_use_acceleration_(false) {} + ViewLayerTest() : widget_(NULL) {} virtual ~ViewLayerTest() { } @@ -3039,9 +3068,6 @@ class ViewLayerTest : public ViewsTestBase { virtual void SetUp() OVERRIDE { ViewTest::SetUp(); - old_use_acceleration_ = View::get_use_acceleration_when_possible(); - View::set_use_acceleration_when_possible(true); - widget_ = new Widget; Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); params.bounds = gfx::Rect(50, 50, 200, 200); @@ -3051,7 +3077,6 @@ class ViewLayerTest : public ViewsTestBase { } virtual void TearDown() OVERRIDE { - View::set_use_acceleration_when_possible(old_use_acceleration_); widget_->CloseNow(); ViewsTestBase::TearDown(); } @@ -3060,7 +3085,6 @@ class ViewLayerTest : public ViewsTestBase { private: Widget* widget_; - bool old_use_acceleration_; }; @@ -3506,17 +3530,6 @@ TEST_F(ViewLayerTest, AcquireLayer) { c1.reset(); } -// Verify that new layer scales content only if the old layer does. -TEST_F(ViewLayerTest, RecreateLayerScaling) { - scoped_ptr<View> v(new View()); - v->SetPaintToLayer(true); - // Set to non default value. - v->layer()->set_scale_content(false); - scoped_ptr<ui::Layer> old_layer(v->RecreateLayer()); - ui::Layer* new_layer = v->layer(); - EXPECT_FALSE(new_layer->scale_content()); -} - // Verify the z-order of the layers as a result of calling RecreateLayer(). TEST_F(ViewLayerTest, RecreateLayerZOrder) { scoped_ptr<View> v(new View()); @@ -3537,13 +3550,13 @@ TEST_F(ViewLayerTest, RecreateLayerZOrder) { scoped_ptr<ui::Layer> v1_old_layer(v1->RecreateLayer()); - // Test the new layer order. |v1_old_layer| should be above the layers + // Test the new layer order. We expect: |v1| |v1_old_layer| |v2|. // for |v1| and |v2|. const std::vector<ui::Layer*>& child_layers_post = v->layer()->children(); ASSERT_EQ(3u, child_layers_post.size()); EXPECT_EQ(v1->layer(), child_layers_post[0]); - EXPECT_EQ(v2->layer(), child_layers_post[1]); - EXPECT_EQ(v1_old_layer, child_layers_post[2]); + EXPECT_EQ(v1_old_layer, child_layers_post[1]); + EXPECT_EQ(v2->layer(), child_layers_post[2]); } // Verify the z-order of the layers as a result of calling RecreateLayer when @@ -3569,15 +3582,393 @@ TEST_F(ViewLayerTest, RecreateLayerZOrderWidgetParent) { scoped_ptr<ui::Layer> v1_old_layer(v1->RecreateLayer()); - // Test the new layer order. |v1_old_layer| should be above the layers - // for |v1| and |v2|. + // Test the new layer order. We expect: |v1| |v1_old_layer| |v2|. const std::vector<ui::Layer*>& child_layers_post = root_layer->children(); ASSERT_EQ(3u, child_layers_post.size()); EXPECT_EQ(v1->layer(), child_layers_post[0]); - EXPECT_EQ(v2->layer(), child_layers_post[1]); - EXPECT_EQ(v1_old_layer, child_layers_post[2]); + EXPECT_EQ(v1_old_layer, child_layers_post[1]); + EXPECT_EQ(v2->layer(), child_layers_post[2]); +} + +// Verifies RecreateLayer() moves all Layers over, even those that don't have +// a View. +TEST_F(ViewLayerTest, RecreateLayerMovesNonViewChildren) { + View v; + v.SetPaintToLayer(true); + View child; + child.SetPaintToLayer(true); + v.AddChildView(&child); + ASSERT_TRUE(v.layer() != NULL); + ASSERT_EQ(1u, v.layer()->children().size()); + EXPECT_EQ(v.layer()->children()[0], child.layer()); + + ui::Layer layer(ui::LAYER_NOT_DRAWN); + v.layer()->Add(&layer); + v.layer()->StackAtBottom(&layer); + + scoped_ptr<ui::Layer> old_layer(v.RecreateLayer()); + + // All children should be moved from old layer to new layer. + ASSERT_TRUE(old_layer.get() != NULL); + EXPECT_TRUE(old_layer->children().empty()); + + // And new layer should have the two children. + ASSERT_TRUE(v.layer() != NULL); + ASSERT_EQ(2u, v.layer()->children().size()); + EXPECT_EQ(v.layer()->children()[0], &layer); + EXPECT_EQ(v.layer()->children()[1], child.layer()); +} + +class BoundsTreeTestView : public View { + public: + BoundsTreeTestView() {} + + virtual void PaintChildren(gfx::Canvas* canvas, + const CullSet& cull_set) OVERRIDE { + // Save out a copy of the cull_set before calling the base implementation. + last_cull_set_.clear(); + if (cull_set.cull_set_) { + for (base::hash_set<intptr_t>::iterator it = cull_set.cull_set_->begin(); + it != cull_set.cull_set_->end(); + ++it) { + last_cull_set_.insert(reinterpret_cast<View*>(*it)); + } + } + View::PaintChildren(canvas, cull_set); + } + + std::set<View*> last_cull_set_; +}; + +TEST_F(ViewLayerTest, BoundsTreePaintUpdatesCullSet) { + BoundsTreeTestView* test_view = new BoundsTreeTestView; + widget()->SetContentsView(test_view); + + View* v1 = new View(); + v1->SetBoundsRect(gfx::Rect(10, 15, 150, 151)); + test_view->AddChildView(v1); + + View* v2 = new View(); + v2->SetBoundsRect(gfx::Rect(20, 33, 40, 50)); + v1->AddChildView(v2); + + // Schedule a full-view paint to get everyone's rectangles updated. + test_view->SchedulePaintInRect(test_view->bounds()); + GetRootLayer()->GetCompositor()->ScheduleDraw(); + ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor()); + + // Now we have test_view - v1 - v2. Damage to only test_view should only + // return root_view and test_view. + test_view->SchedulePaintInRect(gfx::Rect(0, 0, 1, 1)); + GetRootLayer()->GetCompositor()->ScheduleDraw(); + ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor()); + EXPECT_EQ(2U, test_view->last_cull_set_.size()); + EXPECT_EQ(1U, test_view->last_cull_set_.count(widget()->GetRootView())); + EXPECT_EQ(1U, test_view->last_cull_set_.count(test_view)); + + // Damage to v1 only should only return root_view, test_view, and v1. + test_view->SchedulePaintInRect(gfx::Rect(11, 16, 1, 1)); + GetRootLayer()->GetCompositor()->ScheduleDraw(); + ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor()); + EXPECT_EQ(3U, test_view->last_cull_set_.size()); + EXPECT_EQ(1U, test_view->last_cull_set_.count(widget()->GetRootView())); + EXPECT_EQ(1U, test_view->last_cull_set_.count(test_view)); + EXPECT_EQ(1U, test_view->last_cull_set_.count(v1)); + + // A Damage rect inside v2 should get all 3 views back in the |last_cull_set_| + // on call to TestView::Paint(), along with the widget root view. + test_view->SchedulePaintInRect(gfx::Rect(31, 49, 1, 1)); + GetRootLayer()->GetCompositor()->ScheduleDraw(); + ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor()); + EXPECT_EQ(4U, test_view->last_cull_set_.size()); + EXPECT_EQ(1U, test_view->last_cull_set_.count(widget()->GetRootView())); + EXPECT_EQ(1U, test_view->last_cull_set_.count(test_view)); + EXPECT_EQ(1U, test_view->last_cull_set_.count(v1)); + EXPECT_EQ(1U, test_view->last_cull_set_.count(v2)); } -#endif // USE_AURA +TEST_F(ViewLayerTest, BoundsTreeWithRTL) { + std::string locale = l10n_util::GetApplicationLocale(std::string()); + base::i18n::SetICUDefaultLocale("ar"); + + BoundsTreeTestView* test_view = new BoundsTreeTestView; + widget()->SetContentsView(test_view); + + // Add child views, which should be in RTL coordinate space of parent view. + View* v1 = new View; + v1->SetBoundsRect(gfx::Rect(10, 12, 25, 26)); + test_view->AddChildView(v1); + + View* v2 = new View; + v2->SetBoundsRect(gfx::Rect(5, 6, 7, 8)); + v1->AddChildView(v2); + + // Schedule a full-view paint to get everyone's rectangles updated. + test_view->SchedulePaintInRect(test_view->bounds()); + GetRootLayer()->GetCompositor()->ScheduleDraw(); + ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor()); + + // Damage to the right side of the parent view should touch both child views. + gfx::Rect rtl_damage(test_view->bounds().width() - 16, 18, 1, 1); + test_view->SchedulePaintInRect(rtl_damage); + GetRootLayer()->GetCompositor()->ScheduleDraw(); + ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor()); + EXPECT_EQ(4U, test_view->last_cull_set_.size()); + EXPECT_EQ(1U, test_view->last_cull_set_.count(widget()->GetRootView())); + EXPECT_EQ(1U, test_view->last_cull_set_.count(test_view)); + EXPECT_EQ(1U, test_view->last_cull_set_.count(v1)); + EXPECT_EQ(1U, test_view->last_cull_set_.count(v2)); + + // Damage to the left side of the parent view should only touch the + // container views. + gfx::Rect ltr_damage(16, 18, 1, 1); + test_view->SchedulePaintInRect(ltr_damage); + GetRootLayer()->GetCompositor()->ScheduleDraw(); + ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor()); + EXPECT_EQ(2U, test_view->last_cull_set_.size()); + EXPECT_EQ(1U, test_view->last_cull_set_.count(widget()->GetRootView())); + EXPECT_EQ(1U, test_view->last_cull_set_.count(test_view)); + + // Reset locale. + base::i18n::SetICUDefaultLocale(locale); +} + +TEST_F(ViewLayerTest, BoundsTreeSetBoundsChangesCullSet) { + BoundsTreeTestView* test_view = new BoundsTreeTestView; + widget()->SetContentsView(test_view); + + View* v1 = new View; + v1->SetBoundsRect(gfx::Rect(5, 6, 100, 101)); + test_view->AddChildView(v1); + + View* v2 = new View; + v2->SetBoundsRect(gfx::Rect(20, 33, 40, 50)); + v1->AddChildView(v2); + + // Schedule a full-view paint to get everyone's rectangles updated. + test_view->SchedulePaintInRect(test_view->bounds()); + GetRootLayer()->GetCompositor()->ScheduleDraw(); + ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor()); + + // Move v1 to a new origin out of the way of our next query. + v1->SetBoundsRect(gfx::Rect(50, 60, 100, 101)); + // The move will force a repaint. + GetRootLayer()->GetCompositor()->ScheduleDraw(); + ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor()); + + // Schedule a paint with damage rect where v1 used to be. + test_view->SchedulePaintInRect(gfx::Rect(5, 6, 10, 11)); + GetRootLayer()->GetCompositor()->ScheduleDraw(); + ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor()); + + // Should only have picked up root_view and test_view. + EXPECT_EQ(2U, test_view->last_cull_set_.size()); + EXPECT_EQ(1U, test_view->last_cull_set_.count(widget()->GetRootView())); + EXPECT_EQ(1U, test_view->last_cull_set_.count(test_view)); +} + +TEST_F(ViewLayerTest, BoundsTreeLayerChangeMakesNewTree) { + BoundsTreeTestView* test_view = new BoundsTreeTestView; + widget()->SetContentsView(test_view); + + View* v1 = new View; + v1->SetBoundsRect(gfx::Rect(5, 10, 15, 20)); + test_view->AddChildView(v1); + + View* v2 = new View; + v2->SetBoundsRect(gfx::Rect(1, 2, 3, 4)); + v1->AddChildView(v2); + + // Schedule a full-view paint to get everyone's rectangles updated. + test_view->SchedulePaintInRect(test_view->bounds()); + GetRootLayer()->GetCompositor()->ScheduleDraw(); + ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor()); + + // Set v1 to paint to its own layer, it should remove itself from the + // test_view heiarchy and no longer intersect with damage rects in that cull + // set. + v1->SetPaintToLayer(true); + + // Schedule another full-view paint. + test_view->SchedulePaintInRect(test_view->bounds()); + GetRootLayer()->GetCompositor()->ScheduleDraw(); + ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor()); + // v1 and v2 should no longer be present in the test_view cull_set. + EXPECT_EQ(2U, test_view->last_cull_set_.size()); + EXPECT_EQ(0U, test_view->last_cull_set_.count(v1)); + EXPECT_EQ(0U, test_view->last_cull_set_.count(v2)); + + // Now set v1 back to not painting to a layer. + v1->SetPaintToLayer(false); + // Schedule another full-view paint. + test_view->SchedulePaintInRect(test_view->bounds()); + GetRootLayer()->GetCompositor()->ScheduleDraw(); + ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor()); + // We should be back to the full cull set including v1 and v2. + EXPECT_EQ(4U, test_view->last_cull_set_.size()); + EXPECT_EQ(1U, test_view->last_cull_set_.count(widget()->GetRootView())); + EXPECT_EQ(1U, test_view->last_cull_set_.count(test_view)); + EXPECT_EQ(1U, test_view->last_cull_set_.count(v1)); + EXPECT_EQ(1U, test_view->last_cull_set_.count(v2)); +} + +TEST_F(ViewLayerTest, BoundsTreeRemoveChildRemovesBounds) { + BoundsTreeTestView* test_view = new BoundsTreeTestView; + widget()->SetContentsView(test_view); + + View* v1 = new View; + v1->SetBoundsRect(gfx::Rect(5, 10, 15, 20)); + test_view->AddChildView(v1); + + View* v2 = new View; + v2->SetBoundsRect(gfx::Rect(1, 2, 3, 4)); + v1->AddChildView(v2); + + // Schedule a full-view paint to get everyone's rectangles updated. + test_view->SchedulePaintInRect(test_view->bounds()); + GetRootLayer()->GetCompositor()->ScheduleDraw(); + ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor()); + + // Now remove v1 from the root view. + test_view->RemoveChildView(v1); + + // Schedule another full-view paint. + test_view->SchedulePaintInRect(test_view->bounds()); + GetRootLayer()->GetCompositor()->ScheduleDraw(); + ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor()); + // v1 and v2 should no longer be present in the test_view cull_set. + EXPECT_EQ(2U, test_view->last_cull_set_.size()); + EXPECT_EQ(0U, test_view->last_cull_set_.count(v1)); + EXPECT_EQ(0U, test_view->last_cull_set_.count(v2)); + + // View v1 and v2 are no longer part of view hierarchy and therefore won't be + // deleted with that hierarchy. + delete v1; +} + +TEST_F(ViewLayerTest, BoundsTreeMoveViewMovesBounds) { + BoundsTreeTestView* test_view = new BoundsTreeTestView; + widget()->SetContentsView(test_view); + + // Build hierarchy v1 - v2 - v3. + View* v1 = new View; + v1->SetBoundsRect(gfx::Rect(20, 30, 150, 160)); + test_view->AddChildView(v1); + + View* v2 = new View; + v2->SetBoundsRect(gfx::Rect(5, 10, 40, 50)); + v1->AddChildView(v2); + + View* v3 = new View; + v3->SetBoundsRect(gfx::Rect(1, 2, 3, 4)); + v2->AddChildView(v3); + + // Schedule a full-view paint and ensure all views are present in the cull. + test_view->SchedulePaintInRect(test_view->bounds()); + GetRootLayer()->GetCompositor()->ScheduleDraw(); + ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor()); + EXPECT_EQ(5U, test_view->last_cull_set_.size()); + EXPECT_EQ(1U, test_view->last_cull_set_.count(widget()->GetRootView())); + EXPECT_EQ(1U, test_view->last_cull_set_.count(test_view)); + EXPECT_EQ(1U, test_view->last_cull_set_.count(v1)); + EXPECT_EQ(1U, test_view->last_cull_set_.count(v2)); + EXPECT_EQ(1U, test_view->last_cull_set_.count(v3)); + + // Build an unrelated view hierarchy and move v2 in to it. + scoped_ptr<Widget> test_widget(new Widget); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.bounds = gfx::Rect(10, 10, 500, 500); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + test_widget->Init(params); + test_widget->Show(); + BoundsTreeTestView* widget_view = new BoundsTreeTestView; + test_widget->SetContentsView(widget_view); + widget_view->AddChildView(v2); + + // Now schedule full-view paints in both widgets. + test_view->SchedulePaintInRect(test_view->bounds()); + widget_view->SchedulePaintInRect(widget_view->bounds()); + GetRootLayer()->GetCompositor()->ScheduleDraw(); + ui::DrawWaiterForTest::Wait(GetRootLayer()->GetCompositor()); + + // Only v1 should be present in the first cull set. + EXPECT_EQ(3U, test_view->last_cull_set_.size()); + EXPECT_EQ(1U, test_view->last_cull_set_.count(widget()->GetRootView())); + EXPECT_EQ(1U, test_view->last_cull_set_.count(test_view)); + EXPECT_EQ(1U, test_view->last_cull_set_.count(v1)); + + // We should find v2 and v3 in the widget_view cull_set. + EXPECT_EQ(4U, widget_view->last_cull_set_.size()); + EXPECT_EQ(1U, widget_view->last_cull_set_.count(test_widget->GetRootView())); + EXPECT_EQ(1U, widget_view->last_cull_set_.count(widget_view)); + EXPECT_EQ(1U, widget_view->last_cull_set_.count(v2)); + EXPECT_EQ(1U, widget_view->last_cull_set_.count(v3)); +} + +TEST_F(ViewTest, FocusableAssertions) { + // View subclasses may change insets based on whether they are focusable, + // which effects the preferred size. To avoid preferred size changing around + // these Views need to key off the last value set to SetFocusable(), not + // whether the View is focusable right now. For this reason it's important + // that focusable() return the last value passed to SetFocusable and not + // whether the View is focusable right now. + TestView view; + view.SetFocusable(true); + EXPECT_TRUE(view.focusable()); + view.SetEnabled(false); + EXPECT_TRUE(view.focusable()); + view.SetFocusable(false); + EXPECT_FALSE(view.focusable()); +} + +// Verifies when a view is deleted it is removed from ViewStorage. +TEST_F(ViewTest, UpdateViewStorageOnDelete) { + ViewStorage* view_storage = ViewStorage::GetInstance(); + const int storage_id = view_storage->CreateStorageID(); + { + View view; + view_storage->StoreView(storage_id, &view); + } + EXPECT_TRUE(view_storage->RetrieveView(storage_id) == NULL); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeTheme +//////////////////////////////////////////////////////////////////////////////// + +void TestView::OnNativeThemeChanged(const ui::NativeTheme* native_theme) { + native_theme_ = native_theme; +} + +TEST_F(ViewTest, OnNativeThemeChanged) { + TestView* test_view = new TestView(); + EXPECT_FALSE(test_view->native_theme_); + TestView* test_view_child = new TestView(); + EXPECT_FALSE(test_view_child->native_theme_); + + // Child view added before the widget hierarchy exists should get the + // new native theme notification. + test_view->AddChildView(test_view_child); + + scoped_ptr<Widget> widget(new Widget); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget->Init(params); + + widget->GetRootView()->AddChildView(test_view); + EXPECT_TRUE(test_view->native_theme_); + EXPECT_EQ(widget->GetNativeTheme(), test_view->native_theme_); + EXPECT_TRUE(test_view_child->native_theme_); + EXPECT_EQ(widget->GetNativeTheme(), test_view_child->native_theme_); + + // Child view added after the widget hierarchy exists should also get the + // notification. + TestView* test_view_child_2 = new TestView(); + test_view->AddChildView(test_view_child_2); + EXPECT_TRUE(test_view_child_2->native_theme_); + EXPECT_EQ(widget->GetNativeTheme(), test_view_child_2->native_theme_); + + widget->CloseNow(); +} } // namespace views diff --git a/chromium/ui/views/view_unittest_aura.cc b/chromium/ui/views/view_unittest_aura.cc new file mode 100644 index 00000000000..5b649101050 --- /dev/null +++ b/chromium/ui/views/view_unittest_aura.cc @@ -0,0 +1,160 @@ +// Copyright 2014 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/view.h" + +#include "base/memory/scoped_ptr.h" +#include "ui/aura/window.h" +#include "ui/compositor/layer.h" +#include "ui/compositor/layer_tree_owner.h" +#include "ui/compositor/test/test_layers.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/views/test/views_test_base.h" +#include "ui/views/view_constants_aura.h" +#include "ui/views/widget/widget.h" +#include "ui/wm/core/window_util.h" + +namespace views { + +namespace { + +// Creates a widget of TYPE_CONTROL. +// 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.parent = parent; + params.bounds = bounds; + Widget* widget = new Widget(); + widget->Init(params); + return widget; +} + +// Returns a view with a layer with the passed in |bounds| and |layer_name|. +// The caller takes ownership of the returned view. +View* CreateViewWithLayer(const gfx::Rect& bounds, const char* layer_name) { + View* view = new View(); + view->SetBoundsRect(bounds); + view->SetPaintToLayer(true); + view->layer()->set_name(layer_name); + return view; +} + +} // namespace + +typedef ViewsTestBase ViewAuraTest; + +// Test that wm::RecreateLayers() recreates the layers for all child windows and +// all child views and that the z-order of the recreated layers matches that of +// the original layers. +// Test hierarchy: +// w1 +// +-- v1 +// +-- v2 (no layer) +// +-- v3 (no layer) +// +-- v4 +// +-- w2 +// +-- v5 +// +-- v6 +// +-- v7 +// +-- v8 +// +-- v9 +TEST_F(ViewAuraTest, RecreateLayersWithWindows) { + Widget* w1 = CreateControlWidget(GetContext(), gfx::Rect(0, 0, 100, 100)); + w1->GetNativeView()->layer()->set_name("w1"); + + View* v2 = new View(); + v2->SetBounds(0, 1, 100, 101); + View* v3 = new View(); + v3->SetBounds(0, 2, 100, 102); + View* w2_host_view = new View(); + + View* v1 = CreateViewWithLayer(gfx::Rect(0, 3, 100, 103), "v1"); + ui::Layer* v1_layer = v1->layer(); + w1->GetRootView()->AddChildView(v1); + w1->GetRootView()->AddChildView(v2); + v2->AddChildView(v3); + View* v4 = CreateViewWithLayer(gfx::Rect(0, 4, 100, 104), "v4"); + ui::Layer* v4_layer = v4->layer(); + v2->AddChildView(v4); + + w1->GetRootView()->AddChildView(w2_host_view); + View* v7 = CreateViewWithLayer(gfx::Rect(0, 4, 100, 104), "v7"); + ui::Layer* v7_layer = v7->layer(); + w1->GetRootView()->AddChildView(v7); + + View* v8 = CreateViewWithLayer(gfx::Rect(0, 4, 100, 104), "v8"); + ui::Layer* v8_layer = v8->layer(); + v7->AddChildView(v8); + + View* v9 = CreateViewWithLayer(gfx::Rect(0, 4, 100, 104), "v9"); + ui::Layer* v9_layer = v9->layer(); + v7->AddChildView(v9); + + Widget* w2 = + CreateControlWidget(w1->GetNativeView(), gfx::Rect(0, 5, 100, 105)); + w2->GetNativeView()->layer()->set_name("w2"); + w2->GetNativeView()->SetProperty(kHostViewKey, w2_host_view); + + View* v5 = CreateViewWithLayer(gfx::Rect(0, 6, 100, 106), "v5"); + w2->GetRootView()->AddChildView(v5); + View* v6 = CreateViewWithLayer(gfx::Rect(0, 7, 100, 107), "v6"); + ui::Layer* v6_layer = v6->layer(); + v5->AddChildView(v6); + + // Test the initial order of the layers. + ui::Layer* w1_layer = w1->GetNativeView()->layer(); + ASSERT_EQ("w1", w1_layer->name()); + ASSERT_EQ("v1 v4 w2 v7", ui::test::ChildLayerNamesAsString(*w1_layer)); + ui::Layer* w2_layer = w1_layer->children()[2]; + ASSERT_EQ("v5", ui::test::ChildLayerNamesAsString(*w2_layer)); + ui::Layer* v5_layer = w2_layer->children()[0]; + ASSERT_EQ("v6", ui::test::ChildLayerNamesAsString(*v5_layer)); + + { + scoped_ptr<ui::LayerTreeOwner> cloned_owner( + wm::RecreateLayers(w1->GetNativeView())); + EXPECT_EQ(w1_layer, cloned_owner->root()); + EXPECT_NE(w1_layer, w1->GetNativeView()->layer()); + + // The old layers should still exist and have the same hierarchy. + ASSERT_EQ("w1", w1_layer->name()); + ASSERT_EQ("v1 v4 w2 v7", ui::test::ChildLayerNamesAsString(*w1_layer)); + ASSERT_EQ("v5", ui::test::ChildLayerNamesAsString(*w2_layer)); + ASSERT_EQ("v6", ui::test::ChildLayerNamesAsString(*v5_layer)); + EXPECT_EQ("v8 v9", ui::test::ChildLayerNamesAsString(*v7_layer)); + + ASSERT_EQ(4u, w1_layer->children().size()); + EXPECT_EQ(v1_layer, w1_layer->children()[0]); + EXPECT_EQ(v4_layer, w1_layer->children()[1]); + EXPECT_EQ(w2_layer, w1_layer->children()[2]); + EXPECT_EQ(v7_layer, w1_layer->children()[3]); + + ASSERT_EQ(1u, w2_layer->children().size()); + EXPECT_EQ(v5_layer, w2_layer->children()[0]); + + ASSERT_EQ(1u, v5_layer->children().size()); + EXPECT_EQ(v6_layer, v5_layer->children()[0]); + + ASSERT_EQ(0u, v6_layer->children().size()); + + EXPECT_EQ(2u, v7_layer->children().size()); + EXPECT_EQ(v8_layer, v7_layer->children()[0]); + EXPECT_EQ(v9_layer, v7_layer->children()[1]); + + // The cloned layers should have the same hierarchy as old. + ui::Layer* w1_new_layer = w1->GetNativeView()->layer(); + EXPECT_EQ("w1", w1_new_layer->name()); + ASSERT_EQ("v1 v4 w2 v7", ui::test::ChildLayerNamesAsString(*w1_new_layer)); + ui::Layer* w2_new_layer = w1_new_layer->children()[2]; + ASSERT_EQ("v5", ui::test::ChildLayerNamesAsString(*w2_new_layer)); + ui::Layer* v5_new_layer = w2_new_layer->children()[0]; + ASSERT_EQ("v6", ui::test::ChildLayerNamesAsString(*v5_new_layer)); + ui::Layer* v7_new_layer = w1_new_layer->children()[3]; + ASSERT_EQ("v8 v9", ui::test::ChildLayerNamesAsString(*v7_new_layer)); + } + // The views and the widgets are destroyed when AuraTestHelper::TearDown() + // destroys root_window(). +} + +} // namespace views diff --git a/chromium/ui/views/view_win.cc b/chromium/ui/views/view_win.cc deleted file mode 100644 index 183a7758cc3..00000000000 --- a/chromium/ui/views/view_win.cc +++ /dev/null @@ -1,28 +0,0 @@ -// 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/view.h" - -// Necessary to define oleacc GUID's. -#include <windows.h> -#include <initguid.h> -#include <oleacc.h> - -namespace views { - -int View::GetHorizontalDragThreshold() { - static int threshold = -1; - if (threshold == -1) - threshold = GetSystemMetrics(SM_CXDRAG) / 2; - return threshold; -} - -int View::GetVerticalDragThreshold() { - static int threshold = -1; - if (threshold == -1) - threshold = GetSystemMetrics(SM_CYDRAG) / 2; - return threshold; -} - -} // namespace views diff --git a/chromium/ui/views/views.gyp b/chromium/ui/views/views.gyp index af835c22b96..646bb2fcb20 100644 --- a/chromium/ui/views/views.gyp +++ b/chromium/ui/views/views.gyp @@ -8,11 +8,16 @@ 'target_defaults': { 'conditions': [ ['use_aura==1', { - 'sources/': [ ['exclude', '_win\\.(h|cc)$'] ], - 'dependencies': [ '../aura/aura.gyp:aura', ], - }], - ['OS!="linux" or chromeos==1', { - 'sources/': [ ['exclude', '_linux\\.(h|cc)$'] ], + 'dependencies': [ + '../aura/aura.gyp:aura', + '../wm/wm.gyp:wm', + ], + }, { # use_aura==0 + 'sources/': [ + ['exclude', '^corewm/'], + ['exclude', '^touchui/'], + ['exclude', '^widget/desktop_aura/'] + ] }], ], }, @@ -28,20 +33,36 @@ '../../third_party/icu/icu.gyp:icui18n', '../../third_party/icu/icu.gyp:icuuc', '../../url/url.gyp:url_lib', - '../base/strings/ui_strings.gyp:ui_strings', + '../accessibility/accessibility.gyp:accessibility', + '../accessibility/accessibility.gyp:ax_gen', + '../base/ui_base.gyp:ui_base', '../compositor/compositor.gyp:compositor', '../events/events.gyp:events', '../events/events.gyp:events_base', + '../events/platform/events_platform.gyp:events_platform', '../gfx/gfx.gyp:gfx', + '../gfx/gfx.gyp:gfx_geometry', '../native_theme/native_theme.gyp:native_theme', '../resources/ui_resources.gyp:ui_resources', - '../ui.gyp:ui', + '../strings/ui_strings.gyp:ui_strings', + ], + 'export_dependent_settings': [ + '../accessibility/accessibility.gyp:ax_gen', ], 'defines': [ 'VIEWS_IMPLEMENTATION', ], 'sources': [ # All .cc, .h under views, except unittests + 'accessibility/ax_aura_obj_cache.cc', + 'accessibility/ax_aura_obj_cache.h', + 'accessibility/ax_aura_obj_wrapper.h', + 'accessibility/ax_view_obj_wrapper.cc', + 'accessibility/ax_view_obj_wrapper.h', + 'accessibility/ax_widget_obj_wrapper.cc', + 'accessibility/ax_widget_obj_wrapper.h', + 'accessibility/ax_window_obj_wrapper.cc', + 'accessibility/ax_window_obj_wrapper.h', 'accessibility/native_view_accessibility.cc', 'accessibility/native_view_accessibility.h', 'accessibility/native_view_accessibility_win.cc', @@ -62,10 +83,16 @@ 'bubble/bubble_delegate.h', 'bubble/bubble_frame_view.cc', 'bubble/bubble_frame_view.h', + 'bubble/bubble_window_targeter.cc', + 'bubble/bubble_window_targeter.h', 'bubble/tray_bubble_view.cc', 'bubble/tray_bubble_view.h', 'button_drag_utils.cc', 'button_drag_utils.h', + 'cocoa/bridged_content_view.h', + 'cocoa/bridged_content_view.mm', + 'cocoa/bridged_native_widget.h', + 'cocoa/bridged_native_widget.mm', 'color_chooser/color_chooser_listener.h', 'color_chooser/color_chooser_view.cc', 'color_chooser/color_chooser_view.h', @@ -108,21 +135,30 @@ 'controls/link.h', 'controls/link_listener.h', 'controls/menu/display_change_listener_aura.cc', + 'controls/menu/display_change_listener_mac.cc', 'controls/menu/menu.cc', 'controls/menu/menu.h', 'controls/menu/menu_2.cc', 'controls/menu/menu_2.h', 'controls/menu/menu_config.cc', 'controls/menu/menu_config.h', - 'controls/menu/menu_config_views.cc', + 'controls/menu/menu_config_aura.cc', + 'controls/menu/menu_config_mac.cc', 'controls/menu/menu_config_win.cc', 'controls/menu/menu_controller.cc', 'controls/menu/menu_controller.h', - 'controls/menu/menu_controller_aura.cc', 'controls/menu/menu_controller_delegate.h', - 'controls/menu/menu_controller_win.cc', + 'controls/menu/menu_event_dispatcher_linux.cc', + 'controls/menu/menu_event_dispatcher_linux.h', 'controls/menu/menu_delegate.cc', 'controls/menu/menu_delegate.h', + 'controls/menu/menu_message_pump_dispatcher_win.cc', + 'controls/menu/menu_message_pump_dispatcher_win.h', + 'controls/menu/menu_message_loop.h', + 'controls/menu/menu_message_loop_aura.cc', + 'controls/menu/menu_message_loop_aura.h', + 'controls/menu/menu_message_loop_mac.cc', + 'controls/menu/menu_message_loop_mac.h', 'controls/menu/menu_host.cc', 'controls/menu/menu_host.h', 'controls/menu/menu_host_root_view.cc', @@ -142,8 +178,7 @@ 'controls/menu/menu_separator.h', 'controls/menu/menu_separator_views.cc', 'controls/menu/menu_separator_win.cc', - 'controls/menu/menu_win.cc', - 'controls/menu/menu_win.h', + 'controls/menu/menu_types.h', 'controls/menu/menu_wrapper.h', 'controls/menu/native_menu_win.cc', 'controls/menu/native_menu_win.h', @@ -153,16 +188,11 @@ 'controls/menu/submenu_view.h', 'controls/message_box_view.cc', 'controls/message_box_view.h', - 'controls/native_control.cc', - 'controls/native_control.h', - 'controls/native_control_win.cc', - 'controls/native_control_win.h', 'controls/native/native_view_host.cc', 'controls/native/native_view_host.h', 'controls/native/native_view_host_aura.cc', 'controls/native/native_view_host_aura.h', - 'controls/native/native_view_host_win.cc', - 'controls/native/native_view_host_win.h', + 'controls/native/native_view_host_mac.cc', 'controls/prefix_delegate.h', 'controls/prefix_selector.cc', 'controls/prefix_selector.h', @@ -179,8 +209,6 @@ 'controls/scrollbar/base_scroll_bar_button.h', 'controls/scrollbar/base_scroll_bar_thumb.cc', 'controls/scrollbar/base_scroll_bar_thumb.h', - 'controls/scrollbar/bitmap_scroll_bar.cc', - 'controls/scrollbar/bitmap_scroll_bar.h', 'controls/scrollbar/kennedy_scroll_bar.cc', 'controls/scrollbar/kennedy_scroll_bar.h', 'controls/scrollbar/native_scroll_bar_views.cc', @@ -215,46 +243,20 @@ 'controls/table/table_view.h', 'controls/table/table_view_observer.h', 'controls/table/table_view_row_background_painter.h', - 'controls/textfield/native_textfield_views.cc', - 'controls/textfield/native_textfield_views.h', - 'controls/textfield/native_textfield_wrapper.h', 'controls/textfield/textfield.cc', 'controls/textfield/textfield.h', 'controls/textfield/textfield_controller.cc', 'controls/textfield/textfield_controller.h', - 'controls/textfield/textfield_views_model.cc', - 'controls/textfield/textfield_views_model.h', + 'controls/textfield/textfield_model.cc', + 'controls/textfield/textfield_model.h', 'controls/throbber.cc', 'controls/throbber.h', 'controls/tree/tree_view.cc', 'controls/tree/tree_view.h', 'controls/tree/tree_view_controller.cc', 'controls/tree/tree_view_controller.h', - 'corewm/base_focus_rules.cc', - 'corewm/base_focus_rules.h', - 'corewm/capture_controller.cc', - 'corewm/capture_controller.h', - 'corewm/compound_event_filter.cc', - 'corewm/compound_event_filter.h', - 'corewm/corewm_switches.cc', - 'corewm/corewm_switches.h', - 'corewm/cursor_manager.cc', - 'corewm/cursor_manager.h', - 'corewm/focus_controller.cc', - 'corewm/focus_controller.h', - 'corewm/focus_rules.h', - 'corewm/image_grid.cc', - 'corewm/image_grid.h', - 'corewm/input_method_event_filter.cc', - 'corewm/input_method_event_filter.h', - 'corewm/native_cursor_manager.h', - 'corewm/native_cursor_manager_delegate.h', - 'corewm/shadow.cc', - 'corewm/shadow.h', - 'corewm/shadow_controller.cc', - 'corewm/shadow_controller.h', - 'corewm/shadow_types.cc', - 'corewm/shadow_types.h', + 'corewm/cursor_height_provider_win.cc', + 'corewm/cursor_height_provider_win.h', 'corewm/tooltip.h', 'corewm/tooltip_aura.cc', 'corewm/tooltip_aura.h', @@ -262,27 +264,14 @@ 'corewm/tooltip_controller.h', 'corewm/tooltip_win.cc', 'corewm/tooltip_win.h', - 'corewm/transient_window_stacking_client.cc', - 'corewm/transient_window_stacking_client.h', - 'corewm/visibility_controller.cc', - 'corewm/visibility_controller.h', - 'corewm/window_animations.cc', - 'corewm/window_animations.h', - 'corewm/window_modality_controller.cc', - 'corewm/window_modality_controller.h', - 'corewm/window_util.cc', - 'corewm/window_util.h', + 'cull_set.cc', + 'cull_set.h', 'debug_utils.cc', 'debug_utils.h', 'drag_controller.h', 'drag_utils.cc', 'drag_utils.h', - 'event_utils.h', - 'event_utils_aura.cc', - 'event_utils_win.cc', - 'focus/accelerator_handler.h', - 'focus/accelerator_handler_aura.cc', - 'focus/accelerator_handler_win.cc', + 'drag_utils_aura.cc', 'focus/external_focus_tracker.cc', 'focus/external_focus_tracker.h', 'focus/focus_manager.cc', @@ -296,14 +285,16 @@ 'focus/view_storage.h', 'focus/widget_focus_manager.cc', 'focus/widget_focus_manager.h', + 'ime/input_method.h', 'ime/input_method_base.cc', 'ime/input_method_base.h', 'ime/input_method_bridge.cc', 'ime/input_method_bridge.h', 'ime/input_method_delegate.h', - 'ime/input_method.h', 'ime/mock_input_method.cc', 'ime/mock_input_method.h', + 'ime/null_input_method.cc', + 'ime/null_input_method.h', 'layout/box_layout.cc', 'layout/box_layout.h', 'layout/fill_layout.cc', @@ -318,15 +309,21 @@ 'linux_ui/status_icon_linux.h', 'linux_ui/status_icon_linux.cc', 'linux_ui/window_button_order_observer.h', + 'linux_ui/window_button_order_provider.cc', + 'masked_view_targeter.cc', + 'masked_view_targeter.h', 'metrics.cc', 'metrics.h', 'metrics_aura.cc', - 'metrics_win.cc', + 'metrics_mac.cc', 'mouse_constants.h', - 'mouse_watcher.cc', 'mouse_watcher.h', + 'mouse_watcher_aura.cc', 'mouse_watcher_view_host.cc', 'mouse_watcher_view_host.h', + 'native_cursor.h', + 'native_cursor_aura.cc', + 'native_cursor_mac.mm', 'native_theme_delegate.h', 'painter.cc', 'painter.h', @@ -344,7 +341,6 @@ 'touchui/touch_selection_controller_impl.h', 'view.cc', 'view.h', - 'view_aura.cc', 'view_constants.cc', 'view_constants.h', 'view_constants_aura.cc', @@ -353,15 +349,15 @@ 'view_model.h', 'view_model_utils.cc', 'view_model_utils.h', - 'view_win.cc', + 'view_targeter.cc', + 'view_targeter.h', 'views_switches.cc', 'views_switches.h', 'views_delegate.cc', 'views_delegate.h', - 'widget/aero_tooltip_manager.cc', - 'widget/aero_tooltip_manager.h', - 'widget/child_window_message_processor.cc', - 'widget/child_window_message_processor.h', + 'views_touch_selection_controller_factory.h', + 'views_touch_selection_controller_factory_aura.cc', + 'views_touch_selection_controller_factory_mac.cc', 'widget/desktop_aura/desktop_capture_client.cc', 'widget/desktop_aura/desktop_capture_client.h', 'widget/desktop_aura/desktop_cursor_loader_updater.h', @@ -386,12 +382,12 @@ 'widget/desktop_aura/desktop_native_cursor_manager.h', 'widget/desktop_aura/desktop_native_widget_aura.cc', 'widget/desktop_aura/desktop_native_widget_aura.h', - 'widget/desktop_aura/desktop_root_window_host.h', - 'widget/desktop_aura/desktop_root_window_host_ozone.cc', - 'widget/desktop_aura/desktop_root_window_host_win.cc', - 'widget/desktop_aura/desktop_root_window_host_win.h', - 'widget/desktop_aura/desktop_root_window_host_x11.cc', - 'widget/desktop_aura/desktop_root_window_host_x11.h', + 'widget/desktop_aura/desktop_window_tree_host.h', + 'widget/desktop_aura/desktop_window_tree_host_ozone.cc', + 'widget/desktop_aura/desktop_window_tree_host_win.cc', + 'widget/desktop_aura/desktop_window_tree_host_win.h', + 'widget/desktop_aura/desktop_window_tree_host_x11.cc', + 'widget/desktop_aura/desktop_window_tree_host_x11.h', 'widget/desktop_aura/desktop_screen.h', 'widget/desktop_aura/desktop_screen_ozone.cc', 'widget/desktop_aura/desktop_screen_position_client.cc', @@ -404,6 +400,10 @@ 'widget/desktop_aura/x11_desktop_handler.h', 'widget/desktop_aura/x11_desktop_window_move_client.cc', 'widget/desktop_aura/x11_desktop_window_move_client.h', + 'widget/desktop_aura/x11_scoped_capture.cc', + 'widget/desktop_aura/x11_scoped_capture.h', + 'widget/desktop_aura/x11_topmost_window_finder.cc', + 'widget/desktop_aura/x11_topmost_window_finder.h', 'widget/desktop_aura/x11_whole_screen_move_loop.cc', 'widget/desktop_aura/x11_whole_screen_move_loop.h', 'widget/desktop_aura/x11_whole_screen_move_loop_delegate.h', @@ -411,8 +411,6 @@ 'widget/desktop_aura/x11_window_event_filter.h', 'widget/drop_helper.cc', 'widget/drop_helper.h', - 'widget/drop_target_win.cc', - 'widget/drop_target_win.h', 'widget/root_view.cc', 'widget/root_view.h', 'widget/monitor_win.cc', @@ -421,13 +419,11 @@ 'widget/native_widget_aura.cc', 'widget/native_widget_aura.h', 'widget/native_widget_delegate.h', + 'widget/native_widget_mac.h', + 'widget/native_widget_mac.mm', 'widget/native_widget_private.h', - 'widget/native_widget_win.cc', - 'widget/native_widget_win.h', 'widget/tooltip_manager_aura.cc', 'widget/tooltip_manager_aura.h', - 'widget/tooltip_manager_win.cc', - 'widget/tooltip_manager_win.h', 'widget/tooltip_manager.cc', 'widget/tooltip_manager.h', 'widget/widget.cc', @@ -441,10 +437,9 @@ 'widget/widget_hwnd_utils.cc', 'widget/widget_hwnd_utils.h', 'widget/widget_observer.h', + 'widget/widget_removals_observer.h', 'widget/window_reorderer.cc', 'widget/window_reorderer.h', - 'win/appbar.cc', - 'win/appbar.h', 'win/fullscreen_handler.cc', 'win/fullscreen_handler.h', 'win/hwnd_message_handler.cc', @@ -452,7 +447,6 @@ 'win/hwnd_message_handler_delegate.h', 'win/hwnd_util.h', 'win/hwnd_util_aurawin.cc', - 'win/hwnd_util_win.cc', 'win/scoped_fullscreen_visibility.cc', 'win/scoped_fullscreen_visibility.h', 'window/client_view.cc', @@ -470,6 +464,8 @@ 'window/native_frame_view.h', 'window/non_client_view.cc', 'window/non_client_view.h', + 'window/window_button_order_provider.cc', + 'window/window_button_order_provider.h', 'window/window_resources.h', 'window/window_shape.cc', 'window/window_shape.h', @@ -478,53 +474,6 @@ '../../third_party/wtl/include', ], 'conditions': [ - ['use_aura==1', { - 'sources!': [ - 'controls/native_control.cc', - 'controls/native_control.h', - 'controls/scrollbar/bitmap_scroll_bar.cc', - 'controls/scrollbar/bitmap_scroll_bar.h', - 'controls/table/table_view_observer.h', - 'widget/aero_tooltip_manager.cc', - 'widget/aero_tooltip_manager.h', - 'widget/child_window_message_processor.cc', - 'widget/child_window_message_processor.h', - 'widget/tooltip_manager_win.cc', - 'widget/tooltip_manager_win.h', - ], - 'conditions': [ - ['OS=="win"', { - 'sources/': [ - ['include', 'controls/menu/menu_insertion_delegate_win.h'], - ['include', 'controls/menu/native_menu_win.cc'], - ['include', 'controls/menu/native_menu_win.h'], - ['include', 'corewm/tooltip_win.cc'], - ['include', 'corewm/tooltip_win.h'], - ['include', 'event_utils_win.cc'], - ['include', 'widget/desktop_aura/desktop_screen_win.cc'], - ['include', 'widget/desktop_aura/desktop_drag_drop_client_win.cc'], - ['include', 'widget/desktop_aura/desktop_drop_target_win.cc'], - ['include', 'widget/desktop_aura/desktop_root_window_host_win.cc'], - ['include', 'widget/monitor_win.cc'], - ['include', 'widget/monitor_win.h'], - ['include', 'win/appbar.cc'], - ['include', 'win/appbar.h'], - ], - }], - ], - }], - ['use_aura==0', { - 'sources/': [ - ['exclude', 'corewm'], - ['exclude', 'widget/desktop_aura'], - ['exclude', 'widget/window_reorderer.h'], - ['exclude', 'widget/window_reorderer.cc'], - ], - 'sources!': [ - 'widget/widget_aura_utils.cc', - 'widget/widget_aura_utils.h', - ], - }], ['chromeos==1', { 'sources/': [ ['exclude', 'widget/desktop_aura'], @@ -536,24 +485,18 @@ 'bubble/tray_bubble_view.h', ], }], - ['use_aura==0 and OS=="win"', { - 'sources!': [ - 'controls/menu/menu_config_views.cc', - 'controls/menu/menu_separator_views.cc', - ], - }], - ['use_aura==1 and OS=="win"', { - 'sources/': [ - ['include', 'controls/menu/menu_config_win.cc'], - ['include', 'controls/menu/menu_separator_win.cc'], - ['include', 'accessibility/native_view_accessibility_win.cc'], - ['include', 'accessibility/native_view_accessibility_win.h'], + ['chromeos==0 and use_x11==1', { + 'dependencies': [ + '../display/display.gyp:display_util', ], }], ['OS=="linux" and chromeos==0', { 'dependencies': [ '../shell_dialogs/shell_dialogs.gyp:shell_dialogs', ], + 'sources!': [ + 'window/window_button_order_provider.cc', + ], }, { # OS=="linux" and chromeos==0 'sources/': [ ['exclude', 'linux_ui'], @@ -604,6 +547,34 @@ '../ozone/ozone.gyp:ozone', ], }], + ['use_x11==1', { + 'dependencies': [ + '../../build/linux/system.gyp:x11', + '../../build/linux/system.gyp:xrandr', + '../events/platform/x11/x11_events_platform.gyp:x11_events_platform', + ], + }], + ['use_aura==0', { + 'sources!': [ + 'accessibility/ax_aura_obj_cache.cc', + 'accessibility/ax_aura_obj_cache.h', + 'accessibility/ax_aura_obj_wrapper.h', + 'accessibility/ax_view_obj_wrapper.cc', + 'accessibility/ax_view_obj_wrapper.h', + 'accessibility/ax_widget_obj_wrapper.cc', + 'accessibility/ax_widget_obj_wrapper.h', + 'accessibility/ax_window_obj_wrapper.cc', + 'accessibility/ax_window_obj_wrapper.h', + 'bubble/bubble_window_targeter.cc', + 'bubble/bubble_window_targeter.h', + 'bubble/tray_bubble_view.cc', + 'bubble/tray_bubble_view.h', + 'mouse_watcher_view_host.cc', + 'mouse_watcher_view_host.h', + 'widget/window_reorderer.cc', + 'widget/window_reorderer.h', + ], + }], ], }, # target_name: views { @@ -614,38 +585,52 @@ '../../ipc/ipc.gyp:test_support_ipc', '../../skia/skia.gyp:skia', '../../testing/gtest.gyp:gtest', + '../base/ui_base.gyp:ui_base', + '../compositor/compositor.gyp:compositor', '../events/events.gyp:events', + '../events/platform/events_platform.gyp:events_platform', '../gfx/gfx.gyp:gfx', - '../ui.gyp:ui', + '../gfx/gfx.gyp:gfx_geometry', 'views', ], 'include_dirs': [ '..', ], 'sources': [ + 'controls/textfield/textfield_test_api.cc', + 'controls/textfield/textfield_test_api.h', 'corewm/tooltip_controller_test_helper.cc', 'corewm/tooltip_controller_test_helper.h', 'test/capture_tracking_view.cc', 'test/capture_tracking_view.h', - 'test/child_modal_window.cc', - 'test/child_modal_window.h', - 'test/desktop_test_views_delegate.cc', 'test/desktop_test_views_delegate.h', + 'test/desktop_test_views_delegate_aura.cc', + 'test/desktop_test_views_delegate_mac.mm', 'test/menu_runner_test_api.cc', 'test/menu_runner_test_api.h', + 'test/slider_test_api.cc', + 'test/slider_test_api.h', 'test/test_views.cc', 'test/test_views.h', - 'test/test_views_delegate.cc', 'test/test_views_delegate.h', + 'test/test_views_delegate_aura.cc', + 'test/test_views_delegate_mac.mm', 'test/test_widget_observer.cc', 'test/test_widget_observer.h', 'test/ui_controls_factory_desktop_aurax11.cc', 'test/ui_controls_factory_desktop_aurax11.h', 'test/views_test_base.cc', 'test/views_test_base.h', + 'test/views_test_helper.cc', + 'test/views_test_helper.h', + 'test/views_test_helper_aura.cc', + 'test/views_test_helper_aura.h', 'test/widget_test.cc', 'test/widget_test.h', - 'widget/root_view_test_helper.h', + 'test/widget_test_aura.cc', + 'test/widget_test_mac.mm', + 'test/x11_property_change_waiter.cc', + 'test/x11_property_change_waiter.h', ], 'conditions': [ ['chromeos==1', { @@ -657,44 +642,11 @@ ['use_aura==1', { 'dependencies': [ '../aura/aura.gyp:aura_test_support', - '../compositor/compositor.gyp:compositor', - ], - }, { # use_aura==0 - 'sources!': [ - 'corewm/tooltip_controller_test_helper.cc', - 'corewm/tooltip_controller_test_helper.h', - 'test/child_modal_window.cc', - 'test/child_modal_window.h', ], }], ], }, # target_name: views_test_support { - 'target_name': 'views_with_content_test_support', - 'type': 'static_library', - 'dependencies': [ - '../../base/base.gyp:base', - '../../content/content.gyp:content', - '../../content/content_shell_and_tests.gyp:test_support_content', - '../../ipc/ipc.gyp:test_support_ipc', - '../../skia/skia.gyp:skia', - '../../testing/gtest.gyp:gtest', - '../events/events.gyp:events', - '../gfx/gfx.gyp:gfx', - '../ui.gyp:ui', - 'controls/webview/webview.gyp:webview', - 'views_test_support', - 'views', - ], - 'include_dirs': [ - '..', - ], - 'sources': [ - 'test/webview_test_helper.cc', - 'test/webview_test_helper.h', - ], - }, # target_name: views_with_content_test_support - { 'target_name': 'views_unittests', 'type': 'executable', 'dependencies': [ @@ -702,20 +654,22 @@ '../../base/base.gyp:base_i18n', '../../base/base.gyp:test_support_base', '../../skia/skia.gyp:skia', - '../../testing/gmock.gyp:gmock', '../../testing/gtest.gyp:gtest', '../../third_party/icu/icu.gyp:icui18n', '../../third_party/icu/icu.gyp:icuuc', '../../url/url.gyp:url_lib', - '../base/strings/ui_strings.gyp:ui_strings', + '../accessibility/accessibility.gyp:accessibility', + '../base/ui_base.gyp:ui_base', + '../base/ui_base.gyp:ui_base_test_support', '../compositor/compositor.gyp:compositor', '../compositor/compositor.gyp:compositor_test_support', '../events/events.gyp:events', + '../events/events.gyp:events_base', '../gfx/gfx.gyp:gfx', + '../gfx/gfx.gyp:gfx_geometry', '../resources/ui_resources.gyp:ui_resources', '../resources/ui_resources.gyp:ui_test_pak', - '../ui.gyp:ui', - '../ui_unittests.gyp:ui_test_support', + '../strings/ui_strings.gyp:ui_strings', 'views', 'views_test_support', ], @@ -729,12 +683,17 @@ 'bubble/bubble_border_unittest.cc', 'bubble/bubble_delegate_unittest.cc', 'bubble/bubble_frame_view_unittest.cc', + 'bubble/bubble_window_targeter_unittest.cc', + 'cocoa/bridged_native_widget_unittest.mm', + 'controls/button/blue_button_unittest.cc', 'controls/button/custom_button_unittest.cc', 'controls/button/image_button_unittest.cc', 'controls/button/label_button_unittest.cc', + 'controls/button/menu_button_unittest.cc', 'controls/combobox/combobox_unittest.cc', 'controls/label_unittest.cc', 'controls/menu/menu_model_adapter_unittest.cc', + 'controls/menu/menu_controller_unittest.cc', 'controls/native/native_view_host_aura_unittest.cc', 'controls/native/native_view_host_unittest.cc', 'controls/prefix_selector_unittest.cc', @@ -749,26 +708,15 @@ 'controls/table/table_view_unittest.cc', 'controls/table/test_table_model.cc', 'controls/table/test_table_model.h', - 'controls/textfield/native_textfield_views_unittest.cc', - 'controls/textfield/textfield_views_model_unittest.cc', + 'controls/textfield/textfield_unittest.cc', + 'controls/textfield/textfield_model_unittest.cc', 'controls/tree/tree_view_unittest.cc', 'corewm/capture_controller_unittest.cc', - 'corewm/compound_event_filter_unittest.cc', - 'corewm/cursor_manager_unittest.cc', - 'corewm/focus_controller_unittest.cc', - 'corewm/image_grid_unittest.cc', - 'corewm/input_method_event_filter_unittest.cc', - 'corewm/shadow_controller_unittest.cc', 'corewm/tooltip_aura_unittest.cc', 'corewm/tooltip_controller_unittest.cc', - 'corewm/transient_window_stacking_client_unittest.cc', - 'corewm/visibility_controller_unittest.cc', - 'corewm/window_animations_unittest.cc', - 'corewm/window_util_unittest.cc', 'focus/focus_manager_test.h', 'focus/focus_manager_test.cc', 'focus/focus_manager_unittest.cc', - 'focus/focus_manager_unittest_win.cc', 'focus/focus_traversal_unittest.cc', 'ime/input_method_bridge_unittest.cc', 'layout/box_layout_unittest.cc', @@ -778,32 +726,36 @@ 'touchui/touch_selection_controller_impl_unittest.cc', 'view_model_unittest.cc', 'view_model_utils_unittest.cc', + 'view_targeter_unittest.cc', 'view_unittest.cc', + 'view_unittest_aura.cc', + 'widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc', + 'widget/desktop_aura/desktop_focus_rules_unittest.cc', 'widget/desktop_aura/desktop_native_widget_aura_unittest.cc', - 'widget/desktop_aura/desktop_root_window_host_win_unittest.cc', 'widget/desktop_aura/desktop_screen_x11_unittest.cc', 'widget/desktop_aura/desktop_screen_position_client_unittest.cc', + 'widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc', + 'widget/desktop_aura/x11_topmost_window_finder_unittest.cc', 'widget/native_widget_aura_unittest.cc', 'widget/native_widget_unittest.cc', - 'widget/native_widget_win_unittest.cc', 'widget/root_view_unittest.cc', 'widget/widget_unittest.cc', 'widget/window_reorderer_unittest.cc', + 'window/custom_frame_view_unittest.cc', 'window/dialog_client_view_unittest.cc', 'window/dialog_delegate_unittest.cc', ], 'conditions': [ - ['chromeos==0', { - 'sources!': [ - 'touchui/touch_selection_controller_impl_unittest.cc', - ], - }, { # use_chromeos==1 + ['chromeos==1', { 'sources/': [ ['exclude', 'ime/input_method_bridge_unittest.cc'], ['exclude', 'widget/desktop_aura'], ], }], ['OS=="win"', { + 'dependencies': [ + '../../third_party/iaccessible2/iaccessible2.gyp:iaccessible2', + ], 'link_settings': { 'libraries': [ '-limm32.lib', @@ -814,35 +766,30 @@ 'include_dirs': [ '../third_party/wtl/include', ], + 'msvs_settings': { + 'VCManifestTool': { + 'AdditionalManifestFiles': [ + '$(ProjectDir)\\test\\views_unittest.manifest', + ], + }, + }, }], ['OS=="win" and win_use_allocator_shim==1', { 'dependencies': [ '../../base/allocator/allocator.gyp:allocator', ], }], - ['OS=="linux" and linux_use_tcmalloc==1', { + ['OS=="linux" and use_allocator!="none"', { # See http://crbug.com/162998#c4 for why this is needed. 'dependencies': [ '../../base/allocator/allocator.gyp:allocator', ], }], - [ 'use_aura==1', { + ['use_x11==1', { 'dependencies': [ - '../aura/aura.gyp:aura_test_support', - ], - 'sources!': [ - 'widget/native_widget_win_unittest.cc', - ], - }, { # use_aura==0 - 'sources!': [ - 'controls/native/native_view_host_aura_unittest.cc', - 'widget/native_widget_aura_unittest.cc', - ], - 'sources/': [ - ['exclude', 'corewm'], - ['exclude', 'ime/input_method_bridge_unittest.cc'], - ['exclude', 'widget/desktop_aura'], - ['exclude', 'widget/window_reorderer_unittest.cc'] + '../../build/linux/system.gyp:x11', + '../../build/linux/system.gyp:xext', + '../events/platform/x11/x11_events_platform.gyp:x11_events_platform', ], }], ['use_ozone==1', { @@ -850,268 +797,31 @@ 'corewm/capture_controller_unittest.cc', ], }], - # Native tooltip class doesn't run well from unit tests. - ['use_aura==1 and OS=="win"', { - 'sources!': [ - 'corewm/tooltip_controller_unittest.cc', - ], - }], - ], - }, # target_name: views_unittests - { - 'target_name': 'views_examples_lib', - 'type': '<(component)', - 'dependencies': [ - '../../base/base.gyp:base', - '../../skia/skia.gyp:skia', - '../../third_party/icu/icu.gyp:icui18n', - '../../third_party/icu/icu.gyp:icuuc', - '../events/events.gyp:events', - '../gfx/gfx.gyp:gfx', - '../resources/ui_resources.gyp:ui_resources', - '../resources/ui_resources.gyp:ui_test_pak', - '../ui.gyp:ui', - 'views', - ], - 'include_dirs': [ - '..', - ], - 'defines': [ - 'VIEWS_EXAMPLES_IMPLEMENTATION', - ], - 'sources': [ - 'examples/bubble_example.cc', - 'examples/bubble_example.h', - 'examples/button_example.cc', - 'examples/button_example.h', - 'examples/checkbox_example.cc', - 'examples/checkbox_example.h', - 'examples/combobox_example.cc', - 'examples/combobox_example.h', - 'examples/double_split_view_example.cc', - 'examples/double_split_view_example.h', - 'examples/example_base.cc', - 'examples/example_base.h', - 'examples/example_combobox_model.cc', - 'examples/example_combobox_model.h', - 'examples/examples_window.cc', - 'examples/examples_window.h', - 'examples/label_example.cc', - 'examples/label_example.h', - 'examples/link_example.cc', - 'examples/link_example.h', - 'examples/message_box_example.cc', - 'examples/message_box_example.h', - 'examples/menu_example.cc', - 'examples/menu_example.h', - 'examples/multiline_example.cc', - 'examples/multiline_example.h', - 'examples/progress_bar_example.cc', - 'examples/progress_bar_example.h', - 'examples/radio_button_example.cc', - 'examples/radio_button_example.h', - 'examples/scroll_view_example.cc', - 'examples/scroll_view_example.h', - 'examples/single_split_view_example.cc', - 'examples/single_split_view_example.h', - 'examples/slider_example.cc', - 'examples/slider_example.h', - 'examples/tabbed_pane_example.cc', - 'examples/tabbed_pane_example.h', - 'examples/table_example.cc', - 'examples/table_example.h', - 'examples/text_example.cc', - 'examples/text_example.h', - 'examples/textfield_example.cc', - 'examples/textfield_example.h', - 'examples/throbber_example.cc', - 'examples/throbber_example.h', - 'examples/tree_view_example.cc', - 'examples/tree_view_example.h', - 'examples/views_examples_export.h', - 'examples/widget_example.cc', - 'examples/widget_example.h', - ], - 'conditions': [ - ['OS=="win"', { - 'include_dirs': [ - '../third_party/wtl/include', - ], - # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. - 'msvs_disabled_warnings': [ 4267, ], - }], - ], - }, # target_name: views_examples_lib - { - 'target_name': 'views_examples_exe', - 'type': 'executable', - 'dependencies': [ - '../../base/base.gyp:base', - ], - 'sources': [ - 'examples/examples_main.cc', - ], - }, # target_name: views_examples_exe - { - 'target_name': 'views_examples_with_content_lib', - 'type': '<(component)', - 'dependencies': [ - '../../base/base.gyp:base', - '../../base/base.gyp:base_i18n', - '../../content/content.gyp:content', - '../../skia/skia.gyp:skia', - '../../third_party/icu/icu.gyp:icui18n', - '../../third_party/icu/icu.gyp:icuuc', - '../../url/url.gyp:url_lib', - '../events/events.gyp:events', - '../gfx/gfx.gyp:gfx', - '../resources/ui_resources.gyp:ui_resources', - '../resources/ui_resources.gyp:ui_test_pak', - '../ui.gyp:ui', - 'controls/webview/webview.gyp:webview', - 'views', - ], - 'include_dirs': [ - '..', - ], - 'defines': [ - 'VIEWS_EXAMPLES_WITH_CONTENT_IMPLEMENTATION', - ], - 'sources': [ - 'examples/bubble_example.cc', - 'examples/bubble_example.h', - 'examples/button_example.cc', - 'examples/button_example.h', - 'examples/checkbox_example.cc', - 'examples/checkbox_example.h', - 'examples/combobox_example.cc', - 'examples/combobox_example.h', - 'examples/double_split_view_example.cc', - 'examples/double_split_view_example.h', - 'examples/example_base.cc', - 'examples/example_base.h', - 'examples/example_combobox_model.cc', - 'examples/example_combobox_model.h', - 'examples/examples_window_with_content.cc', - 'examples/examples_window_with_content.h', - 'examples/label_example.cc', - 'examples/label_example.h', - 'examples/link_example.cc', - 'examples/link_example.h', - 'examples/message_box_example.cc', - 'examples/message_box_example.h', - 'examples/menu_example.cc', - 'examples/menu_example.h', - 'examples/multiline_example.cc', - 'examples/multiline_example.h', - 'examples/progress_bar_example.cc', - 'examples/progress_bar_example.h', - 'examples/radio_button_example.cc', - 'examples/radio_button_example.h', - 'examples/scroll_view_example.cc', - 'examples/scroll_view_example.h', - 'examples/single_split_view_example.cc', - 'examples/single_split_view_example.h', - 'examples/slider_example.cc', - 'examples/slider_example.h', - 'examples/tabbed_pane_example.cc', - 'examples/tabbed_pane_example.h', - 'examples/table_example.cc', - 'examples/table_example.h', - 'examples/text_example.cc', - 'examples/text_example.h', - 'examples/textfield_example.cc', - 'examples/textfield_example.h', - 'examples/throbber_example.cc', - 'examples/throbber_example.h', - 'examples/tree_view_example.cc', - 'examples/tree_view_example.h', - 'examples/views_examples_with_content_export.h', - 'examples/webview_example.cc', - 'examples/webview_example.h', - 'examples/widget_example.cc', - 'examples/widget_example.h', - ], - 'conditions': [ - ['OS=="win"', { - 'include_dirs': [ - '../third_party/wtl/include', - ], - # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. - 'msvs_disabled_warnings': [ 4267, ], - }], - ], - }, # target_name: views_examples_with_content_lib - { - 'target_name': 'views_examples_with_content_exe', - 'type': 'executable', - 'dependencies': [ - '../../base/base.gyp:base', - '../../base/base.gyp:base_i18n', - '../../content/content.gyp:content', - '../../content/content_shell_and_tests.gyp:content_shell_lib', - '../../content/content_shell_and_tests.gyp:test_support_content', - '../../skia/skia.gyp:skia', - '../../third_party/icu/icu.gyp:icui18n', - '../../third_party/icu/icu.gyp:icuuc', - '../events/events.gyp:events', - '../gfx/gfx.gyp:gfx', - '../resources/ui_resources.gyp:ui_resources', - '../resources/ui_resources.gyp:ui_test_pak', - '../ui.gyp:ui', - '../wm/wm.gyp:wm_test_support', - 'views', - 'views_examples_with_content_lib', - 'views_test_support' - ], - 'include_dirs': [ - '../..', - ], - 'sources': [ - '../../content/app/startup_helper_win.cc', - 'examples/content_client/examples_browser_main_parts.cc', - 'examples/content_client/examples_browser_main_parts.h', - 'examples/content_client/examples_content_browser_client.cc', - 'examples/content_client/examples_content_browser_client.h', - 'examples/content_client/examples_main_delegate.cc', - 'examples/content_client/examples_main_delegate.h', - 'examples/content_client/examples_main.cc', - ], - 'conditions': [ - ['OS=="win"', { - 'link_settings': { - 'libraries': [ - '-limm32.lib', - '-loleacc.lib', - ] - }, - 'msvs_settings': { - 'VCManifestTool': { - 'AdditionalManifestFiles': [ - 'examples\\views_examples.exe.manifest', - ], - }, - 'VCLinkerTool': { - 'SubSystem': '2', # Set /SUBSYSTEM:WINDOWS - }, - }, + ['use_aura==1', { 'dependencies': [ - '../../sandbox/sandbox.gyp:sandbox', + '../aura/aura.gyp:aura_test_support', ], }], - ['use_aura==1', { + ['use_x11==1', { 'dependencies': [ - '../compositor/compositor.gyp:compositor', + '../events/platform/x11/x11_events_platform.gyp:x11_events_platform', ], }], - ['OS=="win"', { - 'sources/': [ - # This is needed because the aura rule strips it from the default - # sources list. - ['include', '^../../content/app/startup_helper_win.cc'], - ], + ['OS=="mac"', { + # views_unittests not yet compiling on Mac. http://crbug.com/378134 + 'sources!': [ + 'bubble/bubble_window_targeter_unittest.cc', + 'controls/button/custom_button_unittest.cc', + 'controls/button/menu_button_unittest.cc', + 'controls/native/native_view_host_unittest.cc', + 'controls/menu/menu_controller_unittest.cc', + 'ime/input_method_bridge_unittest.cc', + 'focus/focus_manager_unittest.cc', + 'widget/window_reorderer_unittest.cc', + 'widget/widget_unittest.cc', + ] }], ], - }, # target_name: views_examples_with_content_exe + }, # target_name: views_unittests ], } diff --git a/chromium/ui/views/views_delegate.cc b/chromium/ui/views/views_delegate.cc index 20cb88f9359..c1c7ca6bd44 100644 --- a/chromium/ui/views/views_delegate.cc +++ b/chromium/ui/views/views_delegate.cc @@ -4,12 +4,12 @@ #include "ui/views/views_delegate.h" -#include "ui/views/touchui/touch_selection_controller_impl.h" +#include "ui/views/views_touch_selection_controller_factory.h" namespace views { ViewsDelegate::ViewsDelegate() - : views_tsc_factory_(new views::ViewsTouchSelectionControllerFactory) { + : views_tsc_factory_(new ViewsTouchSelectionControllerFactory) { ui::TouchSelectionControllerFactory::SetInstance(views_tsc_factory_.get()); } @@ -17,4 +17,81 @@ ViewsDelegate::~ViewsDelegate() { ui::TouchSelectionControllerFactory::SetInstance(NULL); } +void ViewsDelegate::SaveWindowPlacement(const Widget* widget, + const std::string& window_name, + const gfx::Rect& bounds, + ui::WindowShowState show_state) { +} + +bool ViewsDelegate::GetSavedWindowPlacement( + const Widget* widget, + const std::string& window_name, + gfx::Rect* bounds, + ui::WindowShowState* show_state) const { + return false; +} + +void ViewsDelegate::NotifyAccessibilityEvent(View* view, + ui::AXEvent event_type) { +} + +void ViewsDelegate::NotifyMenuItemFocused(const base::string16& menu_name, + const base::string16& menu_item_name, + int item_index, + int item_count, + bool has_submenu) { +} + +#if defined(OS_WIN) +HICON ViewsDelegate::GetDefaultWindowIcon() const { + return NULL; +} + +bool ViewsDelegate::IsWindowInMetro(gfx::NativeWindow window) const { + return false; +} +#elif defined(OS_LINUX) && !defined(OS_CHROMEOS) +gfx::ImageSkia* ViewsDelegate::GetDefaultWindowIcon() const { + return NULL; +} +#endif + +NonClientFrameView* ViewsDelegate::CreateDefaultNonClientFrameView( + Widget* widget) { + return NULL; +} + +void ViewsDelegate::AddRef() { +} + +void ViewsDelegate::ReleaseRef() { +} + +content::WebContents* ViewsDelegate::CreateWebContents( + content::BrowserContext* browser_context, + content::SiteInstance* site_instance) { + return NULL; +} + +base::TimeDelta ViewsDelegate::GetDefaultTextfieldObscuredRevealDuration() { + return base::TimeDelta(); +} + +bool ViewsDelegate::WindowManagerProvidesTitleBar(bool maximized) { + return false; +} + +#if defined(USE_AURA) +ui::ContextFactory* ViewsDelegate::GetContextFactory() { + return NULL; +} +#endif + +#if defined(OS_WIN) +int ViewsDelegate::GetAppbarAutohideEdges(HMONITOR monitor, + const base::Closure& callback) { + return EDGE_BOTTOM; +} +#endif + } // namespace views diff --git a/chromium/ui/views/views_delegate.h b/chromium/ui/views/views_delegate.h index 85fa50b5aa5..0175c8ba201 100644 --- a/chromium/ui/views/views_delegate.h +++ b/chromium/ui/views/views_delegate.h @@ -12,7 +12,7 @@ #endif #include "base/strings/string16.h" -#include "ui/base/accessibility/accessibility_types.h" +#include "ui/accessibility/ax_enums.h" #include "ui/base/ui_base_types.h" #include "ui/gfx/native_widget_types.h" #include "ui/views/views_export.h" @@ -33,6 +33,10 @@ class ImageSkia; class Rect; } +namespace ui { +class ContextFactory; +} + namespace views { class NativeWidget; @@ -52,11 +56,16 @@ class NativeWidgetDelegate; // implementation. class VIEWS_EXPORT ViewsDelegate { public: - // The active ViewsDelegate used by the views system. - static ViewsDelegate* views_delegate; +#if defined(OS_WIN) + enum AppbarAutohideEdge { + EDGE_TOP = 1 << 0, + EDGE_LEFT = 1 << 1, + EDGE_BOTTOM = 1 << 2, + EDGE_RIGHT = 1 << 3, + }; +#endif ViewsDelegate(); - virtual ~ViewsDelegate(); // Saves the position, size and "show" state for the window with the @@ -64,68 +73,86 @@ class VIEWS_EXPORT ViewsDelegate { virtual void SaveWindowPlacement(const Widget* widget, const std::string& window_name, const gfx::Rect& bounds, - ui::WindowShowState show_state) = 0; + ui::WindowShowState show_state); // Retrieves the saved position and size and "show" state for the window with // the specified name. - virtual bool GetSavedWindowPlacement( - const Widget* widget, - const std::string& window_name, - gfx::Rect* bounds, - ui::WindowShowState* show_state) const = 0; + virtual bool GetSavedWindowPlacement(const Widget* widget, + const std::string& window_name, + gfx::Rect* bounds, + ui::WindowShowState* show_state) const; - virtual void NotifyAccessibilityEvent( - View* view, - ui::AccessibilityTypes::Event event_type) = 0; + virtual void NotifyAccessibilityEvent(View* view, ui::AXEvent event_type); // For accessibility, notify the delegate that a menu item was focused // so that alternate feedback (speech / magnified text) can be provided. - virtual void NotifyMenuItemFocused(const string16& menu_name, - const string16& menu_item_name, + virtual void NotifyMenuItemFocused(const base::string16& menu_name, + const base::string16& menu_item_name, int item_index, int item_count, - bool has_submenu) = 0; + bool has_submenu); #if defined(OS_WIN) // Retrieves the default window icon to use for windows if none is specified. - virtual HICON GetDefaultWindowIcon() const = 0; + virtual HICON GetDefaultWindowIcon() const; // Returns true if the window passed in is in the Windows 8 metro // environment. - virtual bool IsWindowInMetro(gfx::NativeWindow window) const = 0; + virtual bool IsWindowInMetro(gfx::NativeWindow window) const; #elif defined(OS_LINUX) && !defined(OS_CHROMEOS) - virtual gfx::ImageSkia* GetDefaultWindowIcon() const = 0; + virtual gfx::ImageSkia* GetDefaultWindowIcon() const; #endif // Creates a default NonClientFrameView to be used for windows that don't // specify their own. If this function returns NULL, the // views::CustomFrameView type will be used. - virtual NonClientFrameView* CreateDefaultNonClientFrameView( - Widget* widget) = 0; - - // Returns whether the embedding app wants windows to be created with the - // views::Widget marked as transparent. For example, an app may wish to - // apply transparent window frames in the NonClientFrameView. - virtual bool UseTransparentWindows() const = 0; + virtual NonClientFrameView* CreateDefaultNonClientFrameView(Widget* widget); // AddRef/ReleaseRef are invoked while a menu is visible. They are used to // ensure we don't attempt to exit while a menu is showing. - virtual void AddRef() = 0; - virtual void ReleaseRef() = 0; + virtual void AddRef(); + virtual void ReleaseRef(); // Creates a web contents. This will return NULL unless overriden. virtual content::WebContents* CreateWebContents( content::BrowserContext* browser_context, - content::SiteInstance* site_instance) = 0; + content::SiteInstance* site_instance); // Gives the platform a chance to modify the properties of a Widget. virtual void OnBeforeWidgetInit(Widget::InitParams* params, internal::NativeWidgetDelegate* delegate) = 0; // Returns the default obscured text reveal duration. - virtual base::TimeDelta GetDefaultTextfieldObscuredRevealDuration() = 0; + virtual base::TimeDelta GetDefaultTextfieldObscuredRevealDuration(); + + // Returns true if the operating system's window manager will always provide a + // title bar with caption buttons (ignoring the setting to + // |remove_standard_frame| in InitParams). If |maximized|, this applies to + // maximized windows; otherwise to restored windows. + virtual bool WindowManagerProvidesTitleBar(bool maximized); + +#if defined(USE_AURA) + // Returns the context factory for new windows. + virtual ui::ContextFactory* GetContextFactory(); +#endif + +#if defined(OS_WIN) + // Starts a query for the appbar autohide edges of the specified monitor and + // returns the current value. If the query finds the edges have changed from + // the current value, |callback| is subsequently invoked. If the edges have + // not changed, |callback| is never run. + // + // The return value is a bitmask of AppbarAutohideEdge. + virtual int GetAppbarAutohideEdges(HMONITOR monitor, + const base::Closure& callback); +#endif + + // The active ViewsDelegate used by the views system. + static ViewsDelegate* views_delegate; private: scoped_ptr<ViewsTouchSelectionControllerFactory> views_tsc_factory_; + + DISALLOW_COPY_AND_ASSIGN(ViewsDelegate); }; } // namespace views diff --git a/chromium/ui/views/views_switches.cc b/chromium/ui/views/views_switches.cc index c62e83d1d8b..342deeec600 100644 --- a/chromium/ui/views/views_switches.cc +++ b/chromium/ui/views/views_switches.cc @@ -16,6 +16,15 @@ namespace switches { const char kDisableViewsRectBasedTargeting[] = "disable-views-rect-based-targeting"; +#if defined(USE_X11) && !defined(OS_CHROMEOS) +// When enabled, tries to get a transparent X11 visual so that we can have +// per-pixel alpha in windows. +// +// TODO(erg): Remove this switch once we've stabilized the code +// path. http://crbug.com/369209 +const char kEnableTransparentVisuals[] = "enable-transparent-visuals"; +#endif + bool IsRectBasedTargetingEnabled() { #if defined(OS_CHROMEOS) || defined(OS_WIN) return !CommandLine::ForCurrentProcess()-> diff --git a/chromium/ui/views/views_switches.h b/chromium/ui/views/views_switches.h index 9e5029476ef..0fb09db6632 100644 --- a/chromium/ui/views/views_switches.h +++ b/chromium/ui/views/views_switches.h @@ -15,6 +15,10 @@ namespace switches { // Please keep alphabetized. VIEWS_EXPORT extern const char kDisableViewsRectBasedTargeting[]; +#if defined(USE_X11) && !defined(OS_CHROMEOS) +VIEWS_EXPORT extern const char kEnableTransparentVisuals[]; +#endif + // Returns true if rect-based targeting in views should be used. VIEWS_EXPORT bool IsRectBasedTargetingEnabled(); diff --git a/chromium/ui/views/views_touch_selection_controller_factory.h b/chromium/ui/views/views_touch_selection_controller_factory.h new file mode 100644 index 00000000000..6b02c1e8d42 --- /dev/null +++ b/chromium/ui/views/views_touch_selection_controller_factory.h @@ -0,0 +1,25 @@ +// Copyright 2014 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_UI_VIEWS_VIEWS_TOUCH_SELECTION_CONTROLLER_FACTORY_H_ +#define UI_UI_VIEWS_VIEWS_TOUCH_SELECTION_CONTROLLER_FACTORY_H_ + +#include "ui/base/touch/touch_editing_controller.h" +#include "ui/views/views_export.h" + +namespace views { + +class VIEWS_EXPORT ViewsTouchSelectionControllerFactory + : public ui::TouchSelectionControllerFactory { + public: + ViewsTouchSelectionControllerFactory(); + + // Overridden from ui::TouchSelectionControllerFactory. + virtual ui::TouchSelectionController* create( + ui::TouchEditable* client_view) OVERRIDE; +}; + +} // namespace views + +#endif // UI_UI_VIEWS_TOUCHUI_TOUCH_SELECTION_CONTROLLER_IMPL_H_ diff --git a/chromium/ui/views/views_touch_selection_controller_factory_aura.cc b/chromium/ui/views/views_touch_selection_controller_factory_aura.cc new file mode 100644 index 00000000000..a28da03a2e4 --- /dev/null +++ b/chromium/ui/views/views_touch_selection_controller_factory_aura.cc @@ -0,0 +1,22 @@ +// Copyright 2014 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/views_touch_selection_controller_factory.h" + +#include "ui/base/ui_base_switches_util.h" +#include "ui/views/touchui/touch_selection_controller_impl.h" + +namespace views { + +ViewsTouchSelectionControllerFactory::ViewsTouchSelectionControllerFactory() { +} + +ui::TouchSelectionController* ViewsTouchSelectionControllerFactory::create( + ui::TouchEditable* client_view) { + if (switches::IsTouchEditingEnabled()) + return new views::TouchSelectionControllerImpl(client_view); + return NULL; +} + +} // namespace views diff --git a/chromium/ui/views/views_touch_selection_controller_factory_mac.cc b/chromium/ui/views/views_touch_selection_controller_factory_mac.cc new file mode 100644 index 00000000000..b4866154e11 --- /dev/null +++ b/chromium/ui/views/views_touch_selection_controller_factory_mac.cc @@ -0,0 +1,17 @@ +// Copyright 2014 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/views_touch_selection_controller_factory.h" + +namespace views { + +ViewsTouchSelectionControllerFactory::ViewsTouchSelectionControllerFactory() { +} + +ui::TouchSelectionController* ViewsTouchSelectionControllerFactory::create( + ui::TouchEditable* client_view) { + return NULL; +} + +} // namespace views diff --git a/chromium/ui/views/widget/aero_tooltip_manager.cc b/chromium/ui/views/widget/aero_tooltip_manager.cc deleted file mode 100644 index 8e1f600b0d5..00000000000 --- a/chromium/ui/views/widget/aero_tooltip_manager.cc +++ /dev/null @@ -1,115 +0,0 @@ -// 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/gfx/point.h" -#include "ui/gfx/win/dpi.h" -#include "ui/gfx/win/hwnd_util.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 = gfx::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 = gfx::win::ScreenToDIPPoint(mouse_pos_in_pixels); - } - if (last_mouse_pos_ != mouse_pos) { - last_mouse_pos_ = mouse_pos; - 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 deleted file mode 100644 index 0f9db20faa4..00000000000 --- a/chromium/ui/views/widget/aero_tooltip_manager.h +++ /dev/null @@ -1,59 +0,0 @@ -// 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 deleted file mode 100644 index dc497560093..00000000000 --- a/chromium/ui/views/widget/child_window_message_processor.cc +++ /dev/null @@ -1,28 +0,0 @@ -// 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 deleted file mode 100644 index 112d9d835d4..00000000000 --- a/chromium/ui/views/widget/child_window_message_processor.h +++ /dev/null @@ -1,47 +0,0 @@ -// 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 index 89630b157dd..c216dcda9de 100644 --- a/chromium/ui/views/widget/desktop_aura/OWNERS +++ b/chromium/ui/views/widget/desktop_aura/OWNERS @@ -1,5 +1,2 @@ # 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 +per-file *x11*=erg@chromium.org diff --git a/chromium/ui/views/widget/desktop_aura/desktop_capture_client.cc b/chromium/ui/views/widget/desktop_aura/desktop_capture_client.cc index 33054fe006d..fe9090d24a2 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_capture_client.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_capture_client.cc @@ -4,8 +4,9 @@ #include "ui/views/widget/desktop_aura/desktop_capture_client.h" -#include "ui/aura/root_window.h" #include "ui/aura/window.h" +#include "ui/aura/window_event_dispatcher.h" +#include "ui/aura/window_tree_host.h" namespace views { @@ -52,7 +53,7 @@ void DesktopCaptureClient::SetCapture(aura::Window* new_capture_window) { capture_window_ = new_capture_window; - aura::client::CaptureDelegate* delegate = root_->GetDispatcher(); + aura::client::CaptureDelegate* delegate = root_->GetHost()->dispatcher(); delegate->UpdateCapture(old_capture_window, new_capture_window); // Initiate native capture updating. @@ -67,7 +68,8 @@ void DesktopCaptureClient::SetCapture(aura::Window* new_capture_window) { for (CaptureClients::iterator i = capture_clients.begin(); i != capture_clients.end(); ++i) { if (*i != this) { - aura::client::CaptureDelegate* delegate = (*i)->root_->GetDispatcher(); + aura::client::CaptureDelegate* delegate = + (*i)->root_->GetHost()->dispatcher(); delegate->OnOtherRootGotCapture(); } } 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 index 599b625b26f..ff116bc81a0 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_cursor_loader_updater.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_cursor_loader_updater.h @@ -33,7 +33,7 @@ class VIEWS_EXPORT DesktopCursorLoaderUpdater { static scoped_ptr<DesktopCursorLoaderUpdater> Create(); // Called when a CursorLoader is created. - virtual void OnCreate(aura::RootWindow* window, + virtual void OnCreate(float device_scale_factor, ui::CursorLoader* loader) = 0; // Called when the display has changed (as we may need to reload the cursor diff --git a/chromium/ui/views/widget/desktop_aura/desktop_cursor_loader_updater_auralinux.cc b/chromium/ui/views/widget/desktop_aura/desktop_cursor_loader_updater_auralinux.cc index 80d99d1094b..63290b31343 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_cursor_loader_updater_auralinux.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_cursor_loader_updater_auralinux.cc @@ -4,7 +4,7 @@ #include "ui/views/widget/desktop_aura/desktop_cursor_loader_updater_auralinux.h" -#include "ui/aura/root_window.h" +#include "ui/aura/window_event_dispatcher.h" #include "ui/base/cursor/cursor_loader.h" #include "ui/base/cursor/cursors_aura.h" #include "ui/gfx/display.h" @@ -53,9 +53,9 @@ DesktopCursorLoaderUpdaterAuraLinux::DesktopCursorLoaderUpdaterAuraLinux() {} DesktopCursorLoaderUpdaterAuraLinux::~DesktopCursorLoaderUpdaterAuraLinux() {} void DesktopCursorLoaderUpdaterAuraLinux::OnCreate( - aura::RootWindow* window, + float device_scale_factor, ui::CursorLoader* loader) { - LoadImageCursors(window->compositor()->device_scale_factor(), loader); + LoadImageCursors(device_scale_factor, loader); } void DesktopCursorLoaderUpdaterAuraLinux::OnDisplayUpdated( diff --git a/chromium/ui/views/widget/desktop_aura/desktop_cursor_loader_updater_auralinux.h b/chromium/ui/views/widget/desktop_aura/desktop_cursor_loader_updater_auralinux.h index 02db2b2773e..c367518ca08 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_cursor_loader_updater_auralinux.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_cursor_loader_updater_auralinux.h @@ -17,7 +17,7 @@ class DesktopCursorLoaderUpdaterAuraLinux : public DesktopCursorLoaderUpdater { virtual ~DesktopCursorLoaderUpdaterAuraLinux(); // Overridden from DesktopCursorLoaderUpdater: - virtual void OnCreate(aura::RootWindow* window, + virtual void OnCreate(float device_scale_factor, ui::CursorLoader* loader) OVERRIDE; virtual void OnDisplayUpdated(const gfx::Display& display, ui::CursorLoader* loader) OVERRIDE; diff --git a/chromium/ui/views/widget/desktop_aura/desktop_dispatcher_client.cc b/chromium/ui/views/widget/desktop_aura/desktop_dispatcher_client.cc index bd4ba0229aa..4b8ca2db08d 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_dispatcher_client.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_dispatcher_client.cc @@ -4,31 +4,30 @@ #include "ui/views/widget/desktop_aura/desktop_dispatcher_client.h" +#include "base/auto_reset.h" +#include "base/bind.h" #include "base/run_loop.h" namespace views { -DesktopDispatcherClient::DesktopDispatcherClient() {} - -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); +DesktopDispatcherClient::~DesktopDispatcherClient() { +} - // DefaultAcceleratorDispatcher dispatcher(nested_dispatcher, - // associated_window); - base::RunLoop run_loop(nested_dispatcher); - run_loop.Run(); - loop->SetNestableTasksAllowed(did_allow_task_nesting); +void DesktopDispatcherClient::PrepareNestedLoopClosures( + base::MessagePumpDispatcher* dispatcher, + base::Closure* run_closure, + base::Closure* quit_closure) { +#if defined(OS_WIN) + scoped_ptr<base::RunLoop> run_loop(new base::RunLoop(dispatcher)); +#else + scoped_ptr<base::RunLoop> run_loop(new base::RunLoop()); +#endif + *quit_closure = run_loop->QuitClosure(); + *run_closure = + base::Bind(&base::RunLoop::Run, base::Owned(run_loop.release())); } } // 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 index 5e7a517cc71..284e81cacda 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_dispatcher_client.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_dispatcher_client.h @@ -6,8 +6,9 @@ #define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DISPATCHER_CLIENT_H_ #include "base/basictypes.h" -#include "ui/aura/client/dispatcher_client.h" +#include "base/callback.h" #include "ui/views/views_export.h" +#include "ui/wm/public/dispatcher_client.h" namespace views { @@ -18,9 +19,10 @@ class VIEWS_EXPORT DesktopDispatcherClient DesktopDispatcherClient(); virtual ~DesktopDispatcherClient(); - virtual void RunWithDispatcher(base::MessageLoop::Dispatcher* dispatcher, - aura::Window* associated_window, - bool nestable_tasks_allowed) OVERRIDE; + virtual void PrepareNestedLoopClosures( + base::MessagePumpDispatcher* dispatcher, + base::Closure* run_closure, + base::Closure* quit_closure) OVERRIDE; private: DISALLOW_COPY_AND_ASSIGN(DesktopDispatcherClient); 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 index 59e46f93245..d8a76e66559 100644 --- 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 @@ -9,18 +9,20 @@ #include "base/event_types.h" #include "base/lazy_instance.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/aura/window_tree_host.h" +#include "ui/base/clipboard/clipboard.h" #include "ui/base/dragdrop/drop_target_event.h" #include "ui/base/dragdrop/os_exchange_data.h" #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h" #include "ui/base/x/selection_utils.h" #include "ui/base/x/x11_util.h" #include "ui/events/event.h" +#include "ui/events/platform/platform_event_source.h" #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" +#include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h" +#include "ui/wm/public/drag_drop_client.h" +#include "ui/wm/public/drag_drop_delegate.h" using aura::client::DragDropDelegate; using ui::OSExchangeData; @@ -35,19 +37,23 @@ const int kWantFurtherPosEvents = 2; const char kXdndActionCopy[] = "XdndActionCopy"; const char kXdndActionMove[] = "XdndActionMove"; const char kXdndActionLink[] = "XdndActionLink"; +const char kXdndActionDirectSave[] = "XdndActionDirectSave"; const char kChromiumDragReciever[] = "_CHROMIUM_DRAG_RECEIVER"; const char kXdndSelection[] = "XdndSelection"; +const char kXdndDirectSave0[] = "XdndDirectSave0"; const char* kAtomsToCache[] = { kChromiumDragReciever, "XdndActionAsk", kXdndActionCopy, + kXdndActionDirectSave, kXdndActionLink, "XdndActionList", kXdndActionMove, "XdndActionPrivate", "XdndAware", + kXdndDirectSave0, "XdndDrop", "XdndEnter", "XdndFinished", @@ -57,88 +63,32 @@ const char* kAtomsToCache[] = { kXdndSelection, "XdndStatus", "XdndTypeList", + ui::Clipboard::kMimeTypeText, NULL }; +// The time to wait for the target to respond after the user has released the +// mouse button before ending the move loop. +const int kEndMoveLoopTimeoutMs = 1000; + +// The time to wait since sending the last XdndPosition message before +// reprocessing the most recent mouse move event in case that the window +// stacking order has changed and |source_current_window_| needs to be updated. +const int kRepeatMouseMoveTimeoutMs = 350; + static base::LazyInstance< std::map< ::Window, views::DesktopDragDropClientAuraX11*> >::Leaky g_live_client_map = LAZY_INSTANCE_INITIALIZER; -// 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 { -class DesktopDragDropClientAuraX11::X11DragContext : - public base::MessageLoop::Dispatcher { +DesktopDragDropClientAuraX11* +DesktopDragDropClientAuraX11::g_current_drag_drop_client = NULL; + +class DesktopDragDropClientAuraX11::X11DragContext + : public ui::PlatformEventDispatcher { public: X11DragContext(ui::X11AtomCache* atom_cache, ::Window local_window, @@ -150,6 +100,7 @@ class DesktopDragDropClientAuraX11::X11DragContext : // message. If we have that data already, dispatch immediately. Otherwise, // delay dispatching until we do. void OnStartXdndPositionMessage(DesktopDragDropClientAuraX11* client, + ::Atom suggested_action, ::Window source_window, const gfx::Point& screen_point); @@ -173,8 +124,13 @@ class DesktopDragDropClientAuraX11::X11DragContext : int GetDragOperation() const; private: - // Overridden from MessageLoop::Dispatcher: - virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE; + // Masks the X11 atom |xdnd_operation|'s views representation onto + // |drag_operation|. + void MaskOpeartion(::Atom xdnd_operation, int* drag_operation) const; + + // ui::PlatformEventDispatcher: + virtual bool CanDispatchEvent(const ui::PlatformEvent& event) OVERRIDE; + virtual uint32_t DispatchEvent(const ui::PlatformEvent& event) OVERRIDE; // The atom cache owned by our parent. ui::X11AtomCache* atom_cache_; @@ -202,6 +158,10 @@ class DesktopDragDropClientAuraX11::X11DragContext : // haven't fetched and put in |fetched_targets_| yet. std::vector<Atom> unfetched_targets_; + // XdndPosition messages have a suggested action. Qt applications exclusively + // use this, instead of the XdndActionList which is backed by |actions_|. + Atom suggested_action_; + // Possible actions. std::vector<Atom> actions_; @@ -216,7 +176,8 @@ DesktopDragDropClientAuraX11::X11DragContext::X11DragContext( local_window_(local_window), source_window_(event.data.l[0]), drag_drop_client_(NULL), - waiting_to_handle_position_(false) { + waiting_to_handle_position_(false), + suggested_action_(None) { bool get_types = ((event.data.l[1] & 1) != 0); if (get_types) { @@ -239,8 +200,7 @@ DesktopDragDropClientAuraX11::X11DragContext::X11DragContext( if (!client) { // The window doesn't have a DesktopDragDropClientAuraX11, that means it's // created by some other process. Listen for messages on it. - base::MessagePumpX11::Current()->AddDispatcherForWindow( - this, source_window_); + ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); XSelectInput(gfx::GetXDisplay(), source_window_, PropertyChangeMask); // We must perform a full sync here because we could be racing @@ -258,20 +218,17 @@ DesktopDragDropClientAuraX11::X11DragContext::X11DragContext( } DesktopDragDropClientAuraX11::X11DragContext::~X11DragContext() { - DesktopDragDropClientAuraX11* client = - DesktopDragDropClientAuraX11::GetForWindow(source_window_); - if (!client) { - // Unsubscribe from message events. - base::MessagePumpX11::Current()->RemoveDispatcherForWindow( - source_window_); - } + // Unsubscribe from message events. + ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); } void DesktopDragDropClientAuraX11::X11DragContext::OnStartXdndPositionMessage( DesktopDragDropClientAuraX11* client, + ::Atom suggested_action, ::Window source_window, const gfx::Point& screen_point) { DCHECK_EQ(source_window_, source_window); + suggested_action_ = suggested_action; if (!unfetched_targets_.empty()) { // We have unfetched targets. That means we need to pause the handling of @@ -301,7 +258,11 @@ void DesktopDragDropClientAuraX11::X11DragContext::RequestNextTarget() { void DesktopDragDropClientAuraX11::X11DragContext::OnSelectionNotify( const XSelectionEvent& event) { - DCHECK(waiting_to_handle_position_); + if (!waiting_to_handle_position_) { + // A misbehaved window may send SelectionNotify without us requesting data + // via XConvertSelection(). + return; + } DCHECK(drag_drop_client_); DCHECK_EQ(event.property, atom_cache_->GetAtom(kChromiumDragReciever)); @@ -345,24 +306,38 @@ 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; + MaskOpeartion(*it, &drag_operation); } + MaskOpeartion(suggested_action_, &drag_operation); + return drag_operation; } -bool DesktopDragDropClientAuraX11::X11DragContext::Dispatch( - const base::NativeEvent& event) { +void DesktopDragDropClientAuraX11::X11DragContext::MaskOpeartion( + ::Atom xdnd_operation, + int* drag_operation) const { + if (xdnd_operation == atom_cache_->GetAtom(kXdndActionCopy)) + *drag_operation |= ui::DragDropTypes::DRAG_COPY; + else if (xdnd_operation == atom_cache_->GetAtom(kXdndActionMove)) + *drag_operation |= ui::DragDropTypes::DRAG_MOVE; + else if (xdnd_operation == atom_cache_->GetAtom(kXdndActionLink)) + *drag_operation |= ui::DragDropTypes::DRAG_LINK; +} + +bool DesktopDragDropClientAuraX11::X11DragContext::CanDispatchEvent( + const ui::PlatformEvent& event) { + return event->xany.window == source_window_; +} + +uint32_t DesktopDragDropClientAuraX11::X11DragContext::DispatchEvent( + const ui::PlatformEvent& event) { if (event->type == PropertyNotify && event->xproperty.atom == atom_cache_->GetAtom("XdndActionList")) { ReadActions(); + return ui::POST_DISPATCH_STOP_PROPAGATION; } - return true; + return ui::POST_DISPATCH_NONE; } /////////////////////////////////////////////////////////////////////////////// @@ -378,17 +353,20 @@ DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11( xwindow_(xwindow), atom_cache_(xdisplay_, kAtomsToCache), target_window_(NULL), + waiting_on_status_(false), + status_received_since_enter_(false), source_provider_(NULL), source_current_window_(None), - drag_drop_in_progress_(false), + source_state_(SOURCE_STATE_OTHER), drag_operation_(0), - resulting_operation_(0), + negotiated_operation_(ui::DragDropTypes::DRAG_NONE), 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.Get().find(xwindow) == - g_live_client_map.Get().end()); - g_live_client_map.Get().insert(std::make_pair(xwindow, this)); + move_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorMove)), + weak_ptr_factory_(this) { + // Some tests change the DesktopDragDropClientAuraX11 associated with an + // |xwindow|. + g_live_client_map.Get()[xwindow] = this; // Mark that we are aware of drag and drop concepts. unsigned long xdnd_version = kMinXdndVersion; @@ -399,6 +377,10 @@ DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11( DesktopDragDropClientAuraX11::~DesktopDragDropClientAuraX11() { g_live_client_map.Get().erase(xwindow_); + // Make sure that all observers are unregistered from source and target + // windows. This may be necessary when the parent native widget gets destroyed + // while a drag operation is in progress. + NotifyDragLeave(); } // static @@ -446,6 +428,7 @@ void DesktopDragDropClientAuraX11::OnXdndPosition( 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; + ::Atom suggested_action = event.data.l[4]; if (!target_current_context_.get()) { NOTREACHED(); @@ -455,7 +438,8 @@ void DesktopDragDropClientAuraX11::OnXdndPosition( // 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)); + this, suggested_action, source_window, + gfx::Point(x_root_window, y_root_window)); } void DesktopDragDropClientAuraX11::OnXdndStatus( @@ -463,14 +447,37 @@ void DesktopDragDropClientAuraX11::OnXdndStatus( DVLOG(1) << "XdndStatus"; unsigned long source_window = event.data.l[0]; - int drag_operation = ui::DragDropTypes::DRAG_NONE; + + if (source_window != source_current_window_) + return; + + if (source_state_ != SOURCE_STATE_PENDING_DROP && + source_state_ != SOURCE_STATE_OTHER) { + return; + } + + waiting_on_status_ = false; + status_received_since_enter_ = true; + if (event.data.l[1] & 1) { ::Atom atom_operation = event.data.l[4]; - negotiated_operation_[source_window] = atom_operation; - drag_operation = AtomToDragOperation(atom_operation); + negotiated_operation_ = AtomToDragOperation(atom_operation); + } else { + negotiated_operation_ = ui::DragDropTypes::DRAG_NONE; + } + + if (source_state_ == SOURCE_STATE_PENDING_DROP) { + // We were waiting on the status message so we could send the XdndDrop. + if (negotiated_operation_ == ui::DragDropTypes::DRAG_NONE) { + move_loop_.EndMoveLoop(); + return; + } + source_state_ = SOURCE_STATE_DROPPED; + SendXdndDrop(source_window); + return; } - switch (drag_operation) { + switch (negotiated_operation_) { case ui::DragDropTypes::DRAG_COPY: move_loop_.UpdateCursor(copy_grab_cursor_); break; @@ -488,31 +495,31 @@ void DesktopDragDropClientAuraX11::OnXdndStatus( // 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()) { + if (next_position_message_.get()) { // 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); + gfx::Point p = next_position_message_->first; + unsigned long event_time = next_position_message_->second; + next_position_message_.reset(); - SendXdndPosition(source_window, p, time); + SendXdndPosition(source_window, p, event_time); } } void DesktopDragDropClientAuraX11::OnXdndFinished( const XClientMessageEvent& event) { DVLOG(1) << "XdndFinished"; - resulting_operation_ = AtomToDragOperation( - negotiated_operation_[event.data.l[0]]); + unsigned long source_window = event.data.l[0]; + if (source_current_window_ != source_window) + return; + + // Clear |negotiated_operation_| if the drag was rejected. + if ((event.data.l[1] & 1) == 0) + negotiated_operation_ = ui::DragDropTypes::DRAG_NONE; + + // Clear |source_current_window_| to avoid sending XdndLeave upon ending the + // move loop. + source_current_window_ = None; move_loop_.EndMoveLoop(); } @@ -555,12 +562,12 @@ void DesktopDragDropClientAuraX11::OnXdndDrop( void DesktopDragDropClientAuraX11::OnSelectionNotify( const XSelectionEvent& xselection) { - if (!target_current_context_) { - NOTIMPLEMENTED(); - return; - } + if (target_current_context_) + target_current_context_->OnSelectionNotify(xselection); - target_current_context_->OnSelectionNotify(xselection); + // ICCCM requires us to delete the property passed into SelectionNotify. + if (xselection.property != None) + XDeleteProperty(xdisplay_, xwindow_, xselection.property); } int DesktopDragDropClientAuraX11::StartDragAndDrop( @@ -571,9 +578,14 @@ int DesktopDragDropClientAuraX11::StartDragAndDrop( int operation, ui::DragDropTypes::DragEventSource source) { source_current_window_ = None; - drag_drop_in_progress_ = true; + DCHECK(!g_current_drag_drop_client); + g_current_drag_drop_client = this; + waiting_on_status_ = false; + next_position_message_.reset(); + status_received_since_enter_ = false; + source_state_ = SOURCE_STATE_OTHER; drag_operation_ = operation; - resulting_operation_ = ui::DragDropTypes::DRAG_NONE; + negotiated_operation_ = ui::DragDropTypes::DRAG_NONE; const ui::OSExchangeData::Provider* provider = &data.provider(); source_provider_ = static_cast<const ui::OSExchangeDataProviderAuraX11*>( @@ -582,22 +594,41 @@ int DesktopDragDropClientAuraX11::StartDragAndDrop( source_provider_->TakeOwnershipOfSelection(); std::vector< ::Atom> actions = GetOfferedDragOperations(); + if (!source_provider_->file_contents_name().empty()) { + actions.push_back(atom_cache_.GetAtom(kXdndActionDirectSave)); + ui::SetStringProperty( + xwindow_, + atom_cache_.GetAtom(kXdndDirectSave0), + atom_cache_.GetAtom(ui::Clipboard::kMimeTypeText), + source_provider_->file_contents_name().AsUTF8Unsafe()); + } ui::SetAtomArrayProperty(xwindow_, "XdndActionList", "ATOM", actions); + // It is possible for the DesktopWindowTreeHostX11 to be destroyed during the + // move loop, which would also destroy this drag-client. So keep track of + // whether it is alive after the drag ends. + base::WeakPtr<DesktopDragDropClientAuraX11> alive( + weak_ptr_factory_.GetWeakPtr()); + // 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_.SetDragImage(source_provider_->GetDragImage(), source_provider_->GetDragImageOffset()); move_loop_.RunMoveLoop(source_window, grab_cursor_); - move_loop_.SetDragImage(gfx::ImageSkia(), gfx::Vector2dF()); - source_provider_ = NULL; - drag_drop_in_progress_ = false; - drag_operation_ = 0; - XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndActionList")); + if (alive) { + move_loop_.SetDragImage(gfx::ImageSkia(), gfx::Vector2dF()); + + source_provider_ = NULL; + g_current_drag_drop_client = NULL; + drag_operation_ = 0; + XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndActionList")); + XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom(kXdndDirectSave0)); - return resulting_operation_; + return negotiated_operation_; + } + return ui::DragDropTypes::DRAG_NONE; } void DesktopDragDropClientAuraX11::DragUpdate(aura::Window* target, @@ -615,7 +646,7 @@ void DesktopDragDropClientAuraX11::DragCancel() { } bool DesktopDragDropClientAuraX11::IsDragDropInProgress() { - return drag_drop_in_progress_; + return !!g_current_drag_drop_client; } void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) { @@ -624,61 +655,170 @@ void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) { } 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_); + repeat_mouse_move_timer_.Stop(); + ProcessMouseMove(gfx::Point(event->x_root, event->y_root), event->time); +} - source_current_window_ = dest_window; +void DesktopDragDropClientAuraX11::OnMouseReleased() { + repeat_mouse_move_timer_.Stop(); - if (source_current_window_ != None) { - negotiated_operation_.erase(source_current_window_); - SendXdndEnter(source_current_window_); - } + if (source_state_ != SOURCE_STATE_OTHER) { + // The user has previously released the mouse and is clicking in + // frustration. + move_loop_.EndMoveLoop(); + return; } 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); - } - } -} + if (waiting_on_status_) { + if (status_received_since_enter_) { + // If we are waiting for an XdndStatus message, we need to wait for it + // to complete. + source_state_ = SOURCE_STATE_PENDING_DROP; + + // Start timer to end the move loop if the target takes too long to send + // the XdndStatus and XdndFinished messages. + StartEndMoveLoopTimer(); + return; + } -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_); + move_loop_.EndMoveLoop(); return; } - std::map< ::Window, ::Atom>::iterator it = - negotiated_operation_.find(source_current_window_); - if (it != negotiated_operation_.end() && it->second != None) { + if (negotiated_operation_ != ui::DragDropTypes::DRAG_NONE) { + // Start timer to end the move loop if the target takes too long to send + // an XdndFinished message. It is important that StartEndMoveLoopTimer() + // is called before SendXdndDrop() because SendXdndDrop() + // sends XdndFinished synchronously if the drop target is a Chrome + // window. + StartEndMoveLoopTimer(); + // We have negotiated an action with the other end. + source_state_ = SOURCE_STATE_DROPPED; SendXdndDrop(source_current_window_); return; } - - SendXdndLeave(source_current_window_); - source_current_window_ = None; } move_loop_.EndMoveLoop(); } void DesktopDragDropClientAuraX11::OnMoveLoopEnded() { + if (source_current_window_ != None) { + SendXdndLeave(source_current_window_); + source_current_window_ = None; + } target_current_context_.reset(); + repeat_mouse_move_timer_.Stop(); + end_move_loop_timer_.Stop(); +} + +XID DesktopDragDropClientAuraX11::FindWindowFor( + const gfx::Point& screen_point) { + views::X11TopmostWindowFinder finder; + ::Window target = finder.FindWindowAt(screen_point); + + if (target == None) + return None; + + // Figure out which window we should test as XdndAware. If |target| has + // XdndProxy, it will set that proxy on target, and if not, |target|'s + // original value will remain. + ui::GetXIDProperty(target, "XdndProxy", &target); + + int version; + if (ui::GetIntProperty(target, "XdndAware", &version) && + version >= kMinXdndVersion) { + return target; + } + return None; +} + +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); +} + +void DesktopDragDropClientAuraX11::ProcessMouseMove( + const gfx::Point& screen_point, + unsigned long event_time) { + if (source_state_ != SOURCE_STATE_OTHER) + return; + + // Find the current window the cursor is over. + ::Window dest_window = FindWindowFor(screen_point); + + if (source_current_window_ != dest_window) { + if (source_current_window_ != None) + SendXdndLeave(source_current_window_); + + source_current_window_ = dest_window; + waiting_on_status_ = false; + next_position_message_.reset(); + status_received_since_enter_ = false; + negotiated_operation_ = ui::DragDropTypes::DRAG_NONE; + + if (source_current_window_ != None) + SendXdndEnter(source_current_window_); + } + + if (source_current_window_ != None) { + if (waiting_on_status_) { + next_position_message_.reset( + new std::pair<gfx::Point, unsigned long>(screen_point, event_time)); + } else { + SendXdndPosition(dest_window, screen_point, event_time); + } + } +} + +void DesktopDragDropClientAuraX11::StartEndMoveLoopTimer() { + end_move_loop_timer_.Start(FROM_HERE, + base::TimeDelta::FromMilliseconds( + kEndMoveLoopTimeoutMs), + this, + &DesktopDragDropClientAuraX11::EndMoveLoop); +} + +void DesktopDragDropClientAuraX11::EndMoveLoop() { + move_loop_.EndMoveLoop(); } void DesktopDragDropClientAuraX11::DragTranslate( @@ -687,8 +827,7 @@ void DesktopDragDropClientAuraX11::DragTranslate( scoped_ptr<ui::DropTargetEvent>* event, aura::client::DragDropDelegate** delegate) { gfx::Point root_location = root_window_location; - root_window_->GetDispatcher()->host()->ConvertPointFromNativeScreen( - &root_location); + root_window_->GetHost()->ConvertPointFromNativeScreen(&root_location); aura::Window* target_window = root_window_->GetEventHandlerForPoint(root_location); bool target_window_changed = false; @@ -747,7 +886,8 @@ void DesktopDragDropClientAuraX11::NotifyDragLeave() { return None; } -int DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) { +ui::DragDropTypes::DragOperation +DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) { if (atom == atom_cache_.GetAtom(kXdndActionCopy)) return ui::DragDropTypes::DRAG_COPY; if (atom == atom_cache_.GetAtom(kXdndActionMove)) @@ -834,12 +974,6 @@ void DesktopDragDropClientAuraX11::SendXdndEnter(::Window dest_window) { } 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"); @@ -856,8 +990,8 @@ void DesktopDragDropClientAuraX11::SendXdndLeave(::Window dest_window) { void DesktopDragDropClientAuraX11::SendXdndPosition( ::Window dest_window, const gfx::Point& screen_point, - unsigned long time) { - waiting_on_status_.insert(dest_window); + unsigned long event_time) { + waiting_on_status_ = true; XEvent xev; xev.xclient.type = ClientMessage; @@ -867,9 +1001,20 @@ void DesktopDragDropClientAuraX11::SendXdndPosition( 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[3] = event_time; xev.xclient.data.l[4] = DragOperationToAtom(drag_operation_); SendXClientEvent(dest_window, &xev); + + // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html and + // the Xdnd protocol both recommend that drag events should be sent + // periodically. + repeat_mouse_move_timer_.Start( + FROM_HERE, + base::TimeDelta::FromMilliseconds(kRepeatMouseMoveTimeoutMs), + base::Bind(&DesktopDragDropClientAuraX11::ProcessMouseMove, + base::Unretained(this), + screen_point, + event_time)); } void DesktopDragDropClientAuraX11::SendXdndDrop(::Window dest_window) { @@ -886,45 +1031,4 @@ void DesktopDragDropClientAuraX11::SendXdndDrop(::Window dest_window) { 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 index 8e8711e5fa3..30f3aa022e1 100644 --- 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 @@ -9,16 +9,18 @@ #include <X11/Xlib.h> #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 "base/memory/weak_ptr.h" +#include "base/timer/timer.h" #include "ui/aura/window_observer.h" #include "ui/base/cursor/cursor.h" +#include "ui/base/dragdrop/drag_drop_types.h" #include "ui/gfx/point.h" #include "ui/gfx/x/x11_atom_cache.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" +#include "ui/wm/public/drag_drop_client.h" namespace aura { namespace client { @@ -42,7 +44,7 @@ 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 +// X11 events forwarded from DesktopWindowTreeHostLinux, while on the other, it // handles the views drag events. class VIEWS_EXPORT DesktopDragDropClientAuraX11 : public aura::client::DragDropClient, @@ -95,9 +97,43 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11 virtual void OnMouseReleased() OVERRIDE; virtual void OnMoveLoopEnded() OVERRIDE; + protected: + // The following methods are virtual for the sake of testing. + + // Finds the topmost X11 window at |screen_point| and returns it if it is + // Xdnd aware. Returns NULL otherwise. + virtual ::Window FindWindowFor(const gfx::Point& screen_point); + + // Sends |xev| to |xid|, optionally short circuiting the round trip to the X + // server. + virtual void SendXClientEvent(::Window xid, XEvent* xev); + private: - typedef std::map< ::Window, std::pair<gfx::Point, unsigned long> > - NextPositionMap; + enum SourceState { + // |source_current_window_| will receive a drop once we receive an + // XdndStatus from it. + SOURCE_STATE_PENDING_DROP, + + // The move looped will be ended once we receive XdndFinished from + // |source_current_window_|. We should not send XdndPosition to + // |source_current_window_| while in this state. + SOURCE_STATE_DROPPED, + + // There is no drag in progress or there is a drag in progress and the + // user has not yet released the mouse. + SOURCE_STATE_OTHER, + }; + + // Processes a mouse move at |screen_point|. + void ProcessMouseMove(const gfx::Point& screen_point, + unsigned long event_time); + + // Start timer to end the move loop if the target is too slow to respond after + // the mouse is released. + void StartEndMoveLoopTimer(); + + // Ends the move loop. + void EndMoveLoop(); // When we receive an position x11 message, we need to translate that into // the underlying aura::Window representation, as moves internal to the X11 @@ -116,7 +152,7 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11 ::Atom DragOperationToAtom(int drag_operation); // Converts a single action atom to a drag operation. - int AtomToDragOperation(::Atom atom); + ui::DragDropTypes::DragOperation 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 @@ -138,13 +174,9 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11 void SendXdndLeave(::Window dest_window); void SendXdndPosition(::Window dest_window, const gfx::Point& screen_point, - unsigned long time); + unsigned long event_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_; @@ -175,46 +207,53 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11 // 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_; + bool 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_; + scoped_ptr<std::pair<gfx::Point, unsigned long> > next_position_message_; + + // Reprocesses the most recent mouse move event if the mouse has not moved + // in a while in case the window stacking order has changed and + // |source_current_window_| needs to be updated. + base::OneShotTimer<DesktopDragDropClientAuraX11> repeat_mouse_move_timer_; + + // When the mouse is released, we need to wait for the last XdndStatus message + // only if we have previously received a status message from + // |source_current_window_|. + bool status_received_since_enter_; // Source side information. ui::OSExchangeDataProviderAuraX11 const* source_provider_; ::Window source_current_window_; + SourceState source_state_; - bool drag_drop_in_progress_; + // The current drag-drop client that has an active operation. Since we have + // multiple root windows and multiple DesktopDragDropClientAuraX11 instances + // it is important to maintain only one drag and drop operation at any time. + static DesktopDragDropClientAuraX11* g_current_drag_drop_client; // 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_; + // is DRAG_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. + ui::DragDropTypes::DragOperation negotiated_operation_; + + // Ends the move loop if the target is too slow to respond after the mouse is + // released. + base::OneShotTimer<DesktopDragDropClientAuraX11> end_move_loop_timer_; // We use these cursors while dragging. gfx::NativeCursor grab_cursor_; gfx::NativeCursor copy_grab_cursor_; gfx::NativeCursor move_grab_cursor_; + base::WeakPtrFactory<DesktopDragDropClientAuraX11> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientAuraX11); }; diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc new file mode 100644 index 00000000000..4286d1873a5 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc @@ -0,0 +1,679 @@ +// Copyright 2014 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 <map> +#include <vector> + +// Include views_test_base.h first because the definition of None in X.h +// conflicts with the definition of None in gtest-type-util.h +#include "ui/views/test/views_test_base.h" + +#include "base/memory/scoped_ptr.h" +#include "base/strings/utf_string_conversions.h" +#include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/base/x/x11_util.h" +#include "ui/gfx/x/x11_atom_cache.h" +#include "ui/gfx/x/x11_types.h" +#include "ui/views/widget/desktop_aura/desktop_cursor_loader_updater.h" +#include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.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/widget.h" + +#include <X11/Xlib.h> + +namespace views { + +namespace { + +const char* kAtomsToCache[] = { + "XdndActionCopy", + "XdndDrop", + "XdndEnter", + "XdndFinished", + "XdndLeave", + "XdndPosition", + "XdndStatus", + "XdndTypeList", + NULL +}; + +class TestDragDropClient; + +// Collects messages which would otherwise be sent to |xid_| via +// SendXClientEvent(). +class ClientMessageEventCollector { + public: + ClientMessageEventCollector(::Window xid, TestDragDropClient* client); + virtual ~ClientMessageEventCollector(); + + // Returns true if |events_| is non-empty. + bool HasEvents() const { + return !events_.empty(); + } + + // Pops all of |events_| and returns the popped events in the order that they + // were on the stack + std::vector<XClientMessageEvent> PopAllEvents(); + + // Adds |event| to the stack. + void RecordEvent(const XClientMessageEvent& event); + + private: + ::Window xid_; + + // Not owned. + TestDragDropClient* client_; + + std::vector<XClientMessageEvent> events_; + + DISALLOW_COPY_AND_ASSIGN(ClientMessageEventCollector); +}; + +// Implementation of DesktopDragDropClientAuraX11 which works with a fake +// |DesktopDragDropClientAuraX11::source_current_window_|. +class TestDragDropClient : public DesktopDragDropClientAuraX11 { + public: + // The location in screen coordinates used for the synthetic mouse moves + // generated in SetTopmostXWindowAndMoveMouse(). + static const int kMouseMoveX; + static const int kMouseMoveY; + + TestDragDropClient(aura::Window* window, + DesktopNativeCursorManager* cursor_manager); + virtual ~TestDragDropClient(); + + // Returns the XID of the window which initiated the drag. + ::Window source_xwindow() { + return source_xid_; + } + + // Returns the Atom with |name|. + Atom GetAtom(const char* name); + + // Returns true if the event's message has |type|. + bool MessageHasType(const XClientMessageEvent& event, + const char* type); + + // Sets |collector| to collect XClientMessageEvents which would otherwise + // have been sent to the drop target window. + void SetEventCollectorFor(::Window xid, + ClientMessageEventCollector* collector); + + // Builds an XdndStatus message and sends it to + // DesktopDragDropClientAuraX11. + void OnStatus(XID target_window, + bool will_accept_drop, + ::Atom accepted_action); + + // Builds an XdndFinished message and sends it to + // DesktopDragDropClientAuraX11. + void OnFinished(XID target_window, + bool accepted_drop, + ::Atom performed_action); + + // Sets |xid| as the topmost window at the current mouse position and + // generates a synthetic mouse move. + void SetTopmostXWindowAndMoveMouse(::Window xid); + + // Returns true if the move loop is running. + bool IsMoveLoopRunning(); + + // DesktopDragDropClientAuraX11: + virtual int StartDragAndDrop( + const ui::OSExchangeData& data, + aura::Window* root_window, + aura::Window* source_window, + const gfx::Point& root_location, + int operation, + ui::DragDropTypes::DragEventSource source) OVERRIDE; + virtual void OnMoveLoopEnded() OVERRIDE; + + private: + // DesktopDragDropClientAuraX11: + virtual ::Window FindWindowFor(const gfx::Point& screen_point) OVERRIDE; + virtual void SendXClientEvent(::Window xid, XEvent* event) OVERRIDE; + + // The XID of the window which initiated the drag. + ::Window source_xid_; + + // The XID of the window which is simulated to be the topmost window at the + // current mouse position. + ::Window target_xid_; + + // Whether the move loop is running. + bool move_loop_running_; + + // Map of ::Windows to the collector which intercepts XClientMessageEvents + // for that window. + std::map< ::Window, ClientMessageEventCollector*> collectors_; + + ui::X11AtomCache atom_cache_; + + DISALLOW_COPY_AND_ASSIGN(TestDragDropClient); +}; + +/////////////////////////////////////////////////////////////////////////////// +// ClientMessageEventCollector + +ClientMessageEventCollector::ClientMessageEventCollector( + ::Window xid, + TestDragDropClient* client) + : xid_(xid), + client_(client) { + client->SetEventCollectorFor(xid, this); +} + +ClientMessageEventCollector::~ClientMessageEventCollector() { + client_->SetEventCollectorFor(xid_, NULL); +} + +std::vector<XClientMessageEvent> ClientMessageEventCollector::PopAllEvents() { + std::vector<XClientMessageEvent> to_return; + to_return.swap(events_); + return to_return; +} + +void ClientMessageEventCollector::RecordEvent( + const XClientMessageEvent& event) { + events_.push_back(event); +} + +/////////////////////////////////////////////////////////////////////////////// +// TestDragDropClient + +// static +const int TestDragDropClient::kMouseMoveX = 100; + +// static +const int TestDragDropClient::kMouseMoveY = 200; + +TestDragDropClient::TestDragDropClient( + aura::Window* window, + DesktopNativeCursorManager* cursor_manager) + : DesktopDragDropClientAuraX11(window, + cursor_manager, + gfx::GetXDisplay(), + window->GetHost()->GetAcceleratedWidget()), + source_xid_(window->GetHost()->GetAcceleratedWidget()), + target_xid_(None), + move_loop_running_(false), + atom_cache_(gfx::GetXDisplay(), kAtomsToCache) { +} + +TestDragDropClient::~TestDragDropClient() { +} + +Atom TestDragDropClient::GetAtom(const char* name) { + return atom_cache_.GetAtom(name); +} + +bool TestDragDropClient::MessageHasType(const XClientMessageEvent& event, + const char* type) { + return event.message_type == atom_cache_.GetAtom(type); +} + +void TestDragDropClient::SetEventCollectorFor( + ::Window xid, + ClientMessageEventCollector* collector) { + if (collector) + collectors_[xid] = collector; + else + collectors_.erase(xid); +} + +void TestDragDropClient::OnStatus(XID target_window, + bool will_accept_drop, + ::Atom accepted_action) { + XClientMessageEvent event; + event.message_type = atom_cache_.GetAtom("XdndStatus"); + event.format = 32; + event.window = source_xid_; + event.data.l[0] = target_window; + event.data.l[1] = will_accept_drop ? 1 : 0; + event.data.l[2] = 0; + event.data.l[3] = 0; + event.data.l[4] = accepted_action; + OnXdndStatus(event); +} + +void TestDragDropClient::OnFinished(XID target_window, + bool accepted_drop, + ::Atom performed_action) { + XClientMessageEvent event; + event.message_type = atom_cache_.GetAtom("XdndFinished"); + event.format = 32; + event.window = source_xid_; + event.data.l[0] = target_window; + event.data.l[1] = accepted_drop ? 1 : 0; + event.data.l[2] = performed_action; + event.data.l[3] = 0; + event.data.l[4] = 0; + OnXdndFinished(event); +} + +void TestDragDropClient::SetTopmostXWindowAndMoveMouse(::Window xid) { + target_xid_ = xid; + + XMotionEvent event; + event.time = CurrentTime; + event.x_root = kMouseMoveX; + event.y_root = kMouseMoveY; + OnMouseMovement(&event); +} + +bool TestDragDropClient::IsMoveLoopRunning() { + return move_loop_running_; +} + +int TestDragDropClient::StartDragAndDrop( + const ui::OSExchangeData& data, + aura::Window* root_window, + aura::Window* source_window, + const gfx::Point& root_location, + int operation, + ui::DragDropTypes::DragEventSource source) { + move_loop_running_ = true; + return DesktopDragDropClientAuraX11::StartDragAndDrop(data, root_window, + source_window, root_location, operation, source); +} + +void TestDragDropClient::OnMoveLoopEnded() { + DesktopDragDropClientAuraX11::OnMoveLoopEnded(); + move_loop_running_ = false; +} + +::Window TestDragDropClient::FindWindowFor(const gfx::Point& screen_point) { + return target_xid_; +} + +void TestDragDropClient::SendXClientEvent(::Window xid, XEvent* event) { + std::map< ::Window, ClientMessageEventCollector*>::iterator it = + collectors_.find(xid); + if (it != collectors_.end()) + it->second->RecordEvent(event->xclient); +} + +} // namespace + +class DesktopDragDropClientAuraX11Test : public ViewsTestBase { + public: + DesktopDragDropClientAuraX11Test() { + } + + virtual ~DesktopDragDropClientAuraX11Test() { + } + + int StartDragAndDrop() { + ui::OSExchangeData data; + data.SetString(base::ASCIIToUTF16("Test")); + + return client_->StartDragAndDrop( + data, + widget_->GetNativeWindow()->GetRootWindow(), + widget_->GetNativeWindow(), + gfx::Point(), + ui::DragDropTypes::DRAG_COPY, + ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE); + } + + // ViewsTestBase: + virtual void SetUp() OVERRIDE { + ViewsTestBase::SetUp(); + + // Create widget to initiate the drags. + widget_.reset(new Widget); + Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.native_widget = new DesktopNativeWidgetAura(widget_.get()); + params.bounds = gfx::Rect(100, 100); + widget_->Init(params); + widget_->Show(); + + cursor_manager_.reset(new DesktopNativeCursorManager( + DesktopCursorLoaderUpdater::Create())); + + client_.reset(new TestDragDropClient(widget_->GetNativeWindow(), + cursor_manager_.get())); + } + + virtual void TearDown() OVERRIDE { + client_.reset(); + cursor_manager_.reset(); + widget_.reset(); + ViewsTestBase::TearDown(); + } + + TestDragDropClient* client() { + return client_.get(); + } + + private: + scoped_ptr<TestDragDropClient> client_; + scoped_ptr<DesktopNativeCursorManager> cursor_manager_; + + // The widget used to initiate drags. + scoped_ptr<Widget> widget_; + + DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientAuraX11Test); +}; + +namespace { + +void BasicStep2(TestDragDropClient* client, XID toplevel) { + EXPECT_TRUE(client->IsMoveLoopRunning()); + + ClientMessageEventCollector collector(toplevel, client); + client->SetTopmostXWindowAndMoveMouse(toplevel); + + // XdndEnter should have been sent to |toplevel| before the XdndPosition + // message. + std::vector<XClientMessageEvent> events = collector.PopAllEvents(); + ASSERT_EQ(2u, events.size()); + + EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); + EXPECT_EQ(client->source_xwindow(), + static_cast<XID>(events[0].data.l[0])); + EXPECT_EQ(1, events[0].data.l[1] & 1); + std::vector<Atom> targets; + ui::GetAtomArrayProperty(client->source_xwindow(), "XdndTypeList", &targets); + EXPECT_FALSE(targets.empty()); + + EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); + EXPECT_EQ(client->source_xwindow(), + static_cast<XID>(events[0].data.l[0])); + const long kCoords = + TestDragDropClient::kMouseMoveX << 16 | TestDragDropClient::kMouseMoveY; + EXPECT_EQ(kCoords, events[1].data.l[2]); + EXPECT_EQ(client->GetAtom("XdndActionCopy"), + static_cast<Atom>(events[1].data.l[4])); + + client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); + + // Because there is no unprocessed XdndPosition, the drag drop client should + // send XdndDrop immediately after the mouse is released. + client->OnMouseReleased(); + + events = collector.PopAllEvents(); + ASSERT_EQ(1u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndDrop")); + EXPECT_EQ(client->source_xwindow(), + static_cast<XID>(events[0].data.l[0])); + + // Send XdndFinished to indicate that the drag drop client can cleanup any + // data related to this drag. The move loop should end only after the + // XdndFinished message was received. + EXPECT_TRUE(client->IsMoveLoopRunning()); + client->OnFinished(toplevel, true, client->GetAtom("XdndActionCopy")); + EXPECT_FALSE(client->IsMoveLoopRunning()); +} + +void BasicStep3(TestDragDropClient* client, XID toplevel) { + EXPECT_TRUE(client->IsMoveLoopRunning()); + + ClientMessageEventCollector collector(toplevel, client); + client->SetTopmostXWindowAndMoveMouse(toplevel); + + std::vector<XClientMessageEvent> events = collector.PopAllEvents(); + ASSERT_EQ(2u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); + EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); + + client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); + client->SetTopmostXWindowAndMoveMouse(toplevel); + events = collector.PopAllEvents(); + ASSERT_EQ(1u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndPosition")); + + // We have not received an XdndStatus ack for the second XdndPosition message. + // Test that sending XdndDrop is delayed till the XdndStatus ack is received. + client->OnMouseReleased(); + EXPECT_FALSE(collector.HasEvents()); + + client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); + events = collector.PopAllEvents(); + ASSERT_EQ(1u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndDrop")); + + EXPECT_TRUE(client->IsMoveLoopRunning()); + client->OnFinished(toplevel, true, client->GetAtom("XdndActionCopy")); + EXPECT_FALSE(client->IsMoveLoopRunning()); +} + +} // namespace + +TEST_F(DesktopDragDropClientAuraX11Test, Basic) { + XID toplevel = 1; + + base::MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&BasicStep2, + client(), + toplevel)); + int result = StartDragAndDrop(); + EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result); + + // Do another drag and drop to test that the data is properly cleaned up as a + // result of the XdndFinished message. + base::MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&BasicStep3, + client(), + toplevel)); + result = StartDragAndDrop(); + EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result); +} + +namespace { + +void TargetDoesNotRespondStep2(TestDragDropClient* client) { + EXPECT_TRUE(client->IsMoveLoopRunning()); + + XID toplevel = 1; + ClientMessageEventCollector collector(toplevel, client); + client->SetTopmostXWindowAndMoveMouse(toplevel); + + std::vector<XClientMessageEvent> events = collector.PopAllEvents(); + ASSERT_EQ(2u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); + EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); + + client->OnMouseReleased(); + events = collector.PopAllEvents(); + ASSERT_EQ(1u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndLeave")); + EXPECT_FALSE(client->IsMoveLoopRunning()); +} + +} // namespace + +// Test that we do not wait for the target to send XdndStatus if we have not +// received any XdndStatus messages at all from the target. The Unity +// DNDCollectionWindow is an example of an XdndAware target which does not +// respond to XdndPosition messages at all. +TEST_F(DesktopDragDropClientAuraX11Test, TargetDoesNotRespond) { + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&TargetDoesNotRespondStep2, client())); + int result = StartDragAndDrop(); + EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result); +} + +namespace { + +void QueuePositionStep2(TestDragDropClient* client) { + EXPECT_TRUE(client->IsMoveLoopRunning()); + + XID toplevel = 1; + ClientMessageEventCollector collector(toplevel, client); + client->SetTopmostXWindowAndMoveMouse(toplevel); + client->SetTopmostXWindowAndMoveMouse(toplevel); + client->SetTopmostXWindowAndMoveMouse(toplevel); + + std::vector<XClientMessageEvent> events = collector.PopAllEvents(); + ASSERT_EQ(2u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); + EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); + + client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); + events = collector.PopAllEvents(); + ASSERT_EQ(1u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndPosition")); + + client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); + EXPECT_FALSE(collector.HasEvents()); + + client->OnMouseReleased(); + events = collector.PopAllEvents(); + ASSERT_EQ(1u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndDrop")); + + EXPECT_TRUE(client->IsMoveLoopRunning()); + client->OnFinished(toplevel, true, client->GetAtom("XdndActionCopy")); + EXPECT_FALSE(client->IsMoveLoopRunning()); +} + +} // namespace + +// Test that XdndPosition messages are queued till the pending XdndPosition +// message is acked via an XdndStatus message. +TEST_F(DesktopDragDropClientAuraX11Test, QueuePosition) { + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&QueuePositionStep2, client())); + int result = StartDragAndDrop(); + EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result); +} + +namespace { + +void TargetChangesStep2(TestDragDropClient* client) { + EXPECT_TRUE(client->IsMoveLoopRunning()); + + XID toplevel1 = 1; + ClientMessageEventCollector collector1(toplevel1, client); + client->SetTopmostXWindowAndMoveMouse(toplevel1); + + std::vector<XClientMessageEvent> events1 = collector1.PopAllEvents(); + ASSERT_EQ(2u, events1.size()); + EXPECT_TRUE(client->MessageHasType(events1[0], "XdndEnter")); + EXPECT_TRUE(client->MessageHasType(events1[1], "XdndPosition")); + + XID toplevel2 = 2; + ClientMessageEventCollector collector2(toplevel2, client); + client->SetTopmostXWindowAndMoveMouse(toplevel2); + + // It is possible for |toplevel1| to send XdndStatus after the source has sent + // XdndLeave but before |toplevel1| has received the XdndLeave message. The + // XdndStatus message should be ignored. + client->OnStatus(toplevel1, true, client->GetAtom("XdndActionCopy")); + events1 = collector1.PopAllEvents(); + ASSERT_EQ(1u, events1.size()); + EXPECT_TRUE(client->MessageHasType(events1[0], "XdndLeave")); + + std::vector<XClientMessageEvent> events2 = collector2.PopAllEvents(); + ASSERT_EQ(2u, events2.size()); + EXPECT_TRUE(client->MessageHasType(events2[0], "XdndEnter")); + EXPECT_TRUE(client->MessageHasType(events2[1], "XdndPosition")); + + client->OnStatus(toplevel2, true, client->GetAtom("XdndActionCopy")); + client->OnMouseReleased(); + events2 = collector2.PopAllEvents(); + ASSERT_EQ(1u, events2.size()); + EXPECT_TRUE(client->MessageHasType(events2[0], "XdndDrop")); + + EXPECT_TRUE(client->IsMoveLoopRunning()); + client->OnFinished(toplevel2, true, client->GetAtom("XdndActionCopy")); + EXPECT_FALSE(client->IsMoveLoopRunning()); +} + +} // namespace + +// Test the behavior when the target changes during a drag. +TEST_F(DesktopDragDropClientAuraX11Test, TargetChanges) { + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&TargetChangesStep2, client())); + int result = StartDragAndDrop(); + EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result); +} + +namespace { + +void RejectAfterMouseReleaseStep2(TestDragDropClient* client) { + EXPECT_TRUE(client->IsMoveLoopRunning()); + + XID toplevel = 1; + ClientMessageEventCollector collector(toplevel, client); + client->SetTopmostXWindowAndMoveMouse(toplevel); + + std::vector<XClientMessageEvent> events = collector.PopAllEvents(); + ASSERT_EQ(2u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); + EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); + + client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); + EXPECT_FALSE(collector.HasEvents()); + + // Send another mouse move such that there is a pending XdndPosition. + client->SetTopmostXWindowAndMoveMouse(toplevel); + events = collector.PopAllEvents(); + ASSERT_EQ(1u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndPosition")); + + client->OnMouseReleased(); + // Reject the drop. + client->OnStatus(toplevel, false, None); + + events = collector.PopAllEvents(); + ASSERT_EQ(1u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndLeave")); + EXPECT_FALSE(client->IsMoveLoopRunning()); +} + +void RejectAfterMouseReleaseStep3(TestDragDropClient* client) { + EXPECT_TRUE(client->IsMoveLoopRunning()); + + XID toplevel = 2; + ClientMessageEventCollector collector(toplevel, client); + client->SetTopmostXWindowAndMoveMouse(toplevel); + + std::vector<XClientMessageEvent> events = collector.PopAllEvents(); + ASSERT_EQ(2u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); + EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); + + client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); + EXPECT_FALSE(collector.HasEvents()); + + client->OnMouseReleased(); + events = collector.PopAllEvents(); + ASSERT_EQ(1u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndDrop")); + + EXPECT_TRUE(client->IsMoveLoopRunning()); + client->OnFinished(toplevel, false, None); + EXPECT_FALSE(client->IsMoveLoopRunning()); +} + +} // namespace + +// Test that the source sends XdndLeave instead of XdndDrop if the drag +// operation is rejected after the mouse is released. +TEST_F(DesktopDragDropClientAuraX11Test, RejectAfterMouseRelease) { + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&RejectAfterMouseReleaseStep2, client())); + int result = StartDragAndDrop(); + EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result); + + // Repeat the test but reject the drop in the XdndFinished message instead. + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&RejectAfterMouseReleaseStep3, client())); + result = StartDragAndDrop(); + EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result); +} + +} // namespace views 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 index 5669f31a1fb..26431c1159f 100644 --- 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 @@ -9,8 +9,7 @@ #include "ui/base/dragdrop/drop_target_event.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" +#include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h" 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 index 042a5a8a3c3..a8051ca8693 100644 --- 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 @@ -7,15 +7,14 @@ #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" +#include "ui/wm/public/drag_drop_client.h" namespace ui { class DragSourceWin; } namespace views { -class DesktopDragDragSourceWin; class DesktopDropTargetWin; class VIEWS_EXPORT DesktopDragDropClientWin 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 index 57c81d58628..1369b62eb79 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.cc @@ -5,14 +5,14 @@ #include "ui/views/widget/desktop_aura/desktop_drop_target_win.h" #include "base/win/win_util.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/aura/window_tree_host.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/drop_target_event.h" #include "ui/base/dragdrop/os_exchange_data_provider_win.h" #include "ui/events/event_constants.h" +#include "ui/wm/public/drag_drop_client.h" +#include "ui/wm/public/drag_drop_delegate.h" using aura::client::DragDropDelegate; using ui::OSExchangeData; @@ -96,7 +96,7 @@ void DesktopDropTargetWin::Translate( DragDropDelegate** delegate) { gfx::Point location(position.x, position.y); gfx::Point root_location = location; - root_window_->GetDispatcher()->host()->ConvertPointFromNativeScreen( + root_window_->GetHost()->ConvertPointFromNativeScreen( &root_location); aura::Window* target_window = root_window_->GetEventHandlerForPoint(root_location); 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 index 113fd75ce23..9e11ff68cf1 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.h @@ -23,7 +23,7 @@ class OSExchangeData; namespace views { // DesktopDropTargetWin takes care of managing drag and drop for -// DesktopRootWindowHostWin. It converts Windows OLE drop messages into +// DesktopWindowTreeHostWin. It converts Windows OLE drop messages into // aura::client::DragDropDelegate calls. class DesktopDropTargetWin : public ui::DropTargetWin, public aura::WindowObserver { diff --git a/chromium/ui/views/widget/desktop_aura/desktop_factory_ozone.h b/chromium/ui/views/widget/desktop_aura/desktop_factory_ozone.h index e557d3bdb7a..2af191b445f 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_factory_ozone.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_factory_ozone.h @@ -9,11 +9,12 @@ namespace gfx { class Rect; +class Screen; } namespace views { class DesktopNativeWidgetAura; -class DesktopRootWindowHost; +class DesktopWindowTreeHost; namespace internal { class NativeWidgetDelegate; @@ -30,12 +31,16 @@ class VIEWS_EXPORT DesktopFactoryOzone { // Sets the implementation delegate. Ownership is retained by the caller. static void SetInstance(DesktopFactoryOzone* impl); - // Delegates implementation of DesktopRootWindowHost::Create externally to + // Delegates implementation of DesktopWindowTreeHost::Create externally to // Ozone implementation. - virtual DesktopRootWindowHost* CreateRootWindowHost( + virtual DesktopWindowTreeHost* CreateWindowTreeHost( internal::NativeWidgetDelegate* native_widget_delegate, DesktopNativeWidgetAura* desktop_native_widget_aura) = 0; + // Delegates implementation of DesktopScreen externally to + // Ozone implementation. + virtual gfx::Screen* CreateDesktopScreen() = 0; + private: static DesktopFactoryOzone* impl_; // not owned }; diff --git a/chromium/ui/views/widget/desktop_aura/desktop_focus_rules.cc b/chromium/ui/views/widget/desktop_aura/desktop_focus_rules.cc index 475facf10cf..1d7d40c5e3d 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_focus_rules.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_focus_rules.cc @@ -4,8 +4,8 @@ #include "ui/views/widget/desktop_aura/desktop_focus_rules.h" -#include "ui/aura/root_window.h" #include "ui/aura/window.h" +#include "ui/aura/window_event_dispatcher.h" namespace views { @@ -14,6 +14,14 @@ DesktopFocusRules::DesktopFocusRules(aura::Window* content_window) DesktopFocusRules::~DesktopFocusRules() {} +bool DesktopFocusRules::CanActivateWindow(aura::Window* window) const { + if (!BaseFocusRules::CanActivateWindow(window)) + return false; + // Never activate a window that is not a child of the root window. Transients + // spanning different DesktopNativeWidgetAuras may trigger this. + return !window || content_window_->GetRootWindow()->Contains(window); +} + bool DesktopFocusRules::SupportsChildActivation(aura::Window* window) const { // In Desktop-Aura, only the content_window or children of the RootWindow are // activatable. @@ -32,7 +40,7 @@ bool DesktopFocusRules::IsWindowConsideredVisibleForActivation( aura::Window* DesktopFocusRules::GetToplevelWindow( aura::Window* window) const { aura::Window* top_level_window = - corewm::BaseFocusRules::GetToplevelWindow(window); + wm::BaseFocusRules::GetToplevelWindow(window); // In Desktop-Aura, only the content_window or children of the RootWindow are // considered as top level windows. if (top_level_window == content_window_->parent()) @@ -43,7 +51,7 @@ aura::Window* DesktopFocusRules::GetToplevelWindow( aura::Window* DesktopFocusRules::GetNextActivatableWindow( aura::Window* window) const { aura::Window* next_activatable_window = - corewm::BaseFocusRules::GetNextActivatableWindow(window); + wm::BaseFocusRules::GetNextActivatableWindow(window); // In Desktop-Aura the content_window_'s parent is a dummy window and thus // should never be activated. We should return the content_window_ if it // can be activated in this case. diff --git a/chromium/ui/views/widget/desktop_aura/desktop_focus_rules.h b/chromium/ui/views/widget/desktop_aura/desktop_focus_rules.h index fff8116b18e..e096e80998d 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_focus_rules.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_focus_rules.h @@ -5,17 +5,18 @@ #ifndef UI_VIEWS_WIDGET_DESKTOP_FOCUS_RULES_H_ #define UI_VIEWS_WIDGET_DESKTOP_FOCUS_RULES_H_ -#include "ui/views/corewm/base_focus_rules.h" +#include "ui/wm/core/base_focus_rules.h" namespace views { -class DesktopFocusRules : public corewm::BaseFocusRules { +class DesktopFocusRules : public wm::BaseFocusRules { public: explicit DesktopFocusRules(aura::Window* content_window); virtual ~DesktopFocusRules(); private: - // Overridden from corewm::BaseFocusRules: + // Overridden from wm::BaseFocusRules: + virtual bool CanActivateWindow(aura::Window* window) const OVERRIDE; virtual bool SupportsChildActivation(aura::Window* window) const OVERRIDE; virtual bool IsWindowConsideredVisibleForActivation( aura::Window* window) const OVERRIDE; diff --git a/chromium/ui/views/widget/desktop_aura/desktop_focus_rules_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_focus_rules_unittest.cc new file mode 100644 index 00000000000..39eda7dcb47 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_focus_rules_unittest.cc @@ -0,0 +1,58 @@ +// Copyright 2014 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/client/focus_client.h" +#include "ui/aura/test/test_window_delegate.h" +#include "ui/aura/window.h" +#include "ui/aura/window_event_dispatcher.h" +#include "ui/aura/window_layer_type.h" +#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/wm/core/window_util.h" + +namespace views { + +namespace { + +scoped_ptr<Widget> CreateDesktopWidget() { + scoped_ptr<Widget> widget(new Widget); + Widget::InitParams params = Widget::InitParams( + Widget::InitParams::TYPE_WINDOW); + params.bounds = gfx::Rect(0, 0, 200, 200); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.native_widget = new DesktopNativeWidgetAura(widget.get()); + widget->Init(params); + return widget.Pass(); +} + +} // namespace + +typedef ViewsTestBase DesktopFocusRulesTest; + +// Verifies we don't attempt to activate a window in another widget. +TEST_F(DesktopFocusRulesTest, DontFocusWindowsInOtherHierarchies) { + // Two widgets (each with a DesktopNativeWidgetAura). |w2| has a child Window + // |w2_child| that is not focusable. |w2_child|'s has a transient parent in + // |w1|. + scoped_ptr<views::Widget> w1(CreateDesktopWidget()); + scoped_ptr<views::Widget> w2(CreateDesktopWidget()); + aura::test::TestWindowDelegate w2_child_delegate; + w2_child_delegate.set_can_focus(false); + aura::Window* w2_child = new aura::Window(&w2_child_delegate); + w2_child->Init(aura::WINDOW_LAYER_SOLID_COLOR); + w2->GetNativeView()->AddChild(w2_child); + wm::AddTransientChild(w1->GetNativeView(), w2_child); + aura::client::GetFocusClient(w2->GetNativeView())->FocusWindow(w2_child); + aura::Window* focused = + aura::client::GetFocusClient(w2->GetNativeView())->GetFocusedWindow(); + EXPECT_TRUE((focused == NULL) || w2->GetNativeView()->Contains(focused)); + wm::RemoveTransientChild(w1->GetNativeView(), w2_child); + w1.reset(); + w2.reset(); +} + +} // namespace views 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 index 911f7753361..187aa6e612f 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_native_cursor_manager.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_native_cursor_manager.cc @@ -4,20 +4,19 @@ #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" -#include "ui/aura/root_window.h" +#include "ui/aura/window_event_dispatcher.h" +#include "ui/aura/window_tree_host.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_updater_(cursor_loader_updater.Pass()), cursor_loader_(ui::CursorLoader::Create()) { if (cursor_loader_updater_.get()) - cursor_loader_updater_->OnCreate(root_window_, cursor_loader_.get()); + cursor_loader_updater_->OnCreate(1.0f, cursor_loader_.get()); } DesktopNativeCursorManager::~DesktopNativeCursorManager() { @@ -29,11 +28,20 @@ gfx::NativeCursor DesktopNativeCursorManager::GetInitializedCursor(int type) { return cursor; } +void DesktopNativeCursorManager::AddHost(aura::WindowTreeHost* host) { + hosts_.insert(host); +} + +void DesktopNativeCursorManager::RemoveHost(aura::WindowTreeHost* host) { + hosts_.erase(host); +} + void DesktopNativeCursorManager::SetDisplay( const gfx::Display& display, - views::corewm::NativeCursorManagerDelegate* delegate) { + wm::NativeCursorManagerDelegate* delegate) { cursor_loader_->UnloadAll(); - cursor_loader_->set_display(display); + cursor_loader_->set_rotation(display.rotation()); + cursor_loader_->set_scale(display.device_scale_factor()); if (cursor_loader_updater_.get()) cursor_loader_updater_->OnDisplayUpdated(display, cursor_loader_.get()); @@ -43,18 +51,20 @@ void DesktopNativeCursorManager::SetDisplay( void DesktopNativeCursorManager::SetCursor( gfx::NativeCursor cursor, - views::corewm::NativeCursorManagerDelegate* delegate) { + wm::NativeCursorManagerDelegate* delegate) { gfx::NativeCursor new_cursor = cursor; cursor_loader_->SetPlatformCursor(&new_cursor); delegate->CommitCursor(new_cursor); - if (delegate->IsCursorVisible()) - root_window_->SetCursor(new_cursor); + if (delegate->IsCursorVisible()) { + for (Hosts::const_iterator i = hosts_.begin(); i != hosts_.end(); ++i) + (*i)->SetCursor(new_cursor); + } } void DesktopNativeCursorManager::SetVisibility( bool visible, - views::corewm::NativeCursorManagerDelegate* delegate) { + wm::NativeCursorManagerDelegate* delegate) { delegate->CommitVisibility(visible); if (visible) { @@ -62,27 +72,23 @@ void DesktopNativeCursorManager::SetVisibility( } else { gfx::NativeCursor invisible_cursor(ui::kCursorNone); cursor_loader_->SetPlatformCursor(&invisible_cursor); - root_window_->SetCursor(invisible_cursor); + for (Hosts::const_iterator i = hosts_.begin(); i != hosts_.end(); ++i) + (*i)->SetCursor(invisible_cursor); } - root_window_->OnCursorVisibilityChanged(visible); + for (Hosts::const_iterator i = hosts_.begin(); i != hosts_.end(); ++i) + (*i)->OnCursorVisibilityChanged(visible); } void DesktopNativeCursorManager::SetCursorSet( ui::CursorSetType cursor_set, - views::corewm::NativeCursorManagerDelegate* delegate) { - NOTIMPLEMENTED(); -} - -void DesktopNativeCursorManager::SetScale( - float scale, - views::corewm::NativeCursorManagerDelegate* delegate) { + wm::NativeCursorManagerDelegate* delegate) { NOTIMPLEMENTED(); } void DesktopNativeCursorManager::SetMouseEventsEnabled( bool enabled, - views::corewm::NativeCursorManagerDelegate* delegate) { + wm::NativeCursorManagerDelegate* delegate) { delegate->CommitMouseEventsEnabled(enabled); // TODO(erg): In the ash version, we set the last mouse location on Env. I'm @@ -90,7 +96,8 @@ void DesktopNativeCursorManager::SetMouseEventsEnabled( SetVisibility(delegate->IsCursorVisible(), delegate); - root_window_->OnMouseEventsEnableStateChanged(enabled); + for (Hosts::const_iterator i = hosts_.begin(); i != hosts_.end(); ++i) + (*i)->dispatcher()->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 index 563131d92cc..ee156348774 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_native_cursor_manager.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_native_cursor_manager.h @@ -5,61 +5,69 @@ #ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_NATIVE_CURSOR_MANAGER_H_ #define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_NATIVE_CURSOR_MANAGER_H_ +#include <set> + #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" +#include "ui/wm/core/native_cursor_manager.h" namespace aura { -class RootWindow; +class WindowTreeHost; } namespace ui { class CursorLoader; } -namespace views { -class DesktopCursorLoaderUpdater; - -namespace corewm { +namespace wm { 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.) +namespace views { +class DesktopCursorLoaderUpdater; + +// A NativeCursorManager that performs the desktop-specific setting of cursor +// state. Similar to AshNativeCursorManager, it also communicates these changes +// to all root windows. class VIEWS_EXPORT DesktopNativeCursorManager - : public views::corewm::NativeCursorManager { + : public wm::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); + // Adds |host| to the set |hosts_|. + void AddHost(aura::WindowTreeHost* host); + + // Removes |host| from the set |hosts_|. + void RemoveHost(aura::WindowTreeHost* host); + private: - // Overridden from views::corewm::NativeCursorManager: + // Overridden from wm::NativeCursorManager: virtual void SetDisplay( const gfx::Display& display, - views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE; + wm::NativeCursorManagerDelegate* delegate) OVERRIDE; virtual void SetCursor( gfx::NativeCursor cursor, - views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE; + wm::NativeCursorManagerDelegate* delegate) OVERRIDE; virtual void SetVisibility( bool visible, - views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE; + wm::NativeCursorManagerDelegate* delegate) OVERRIDE; virtual void SetCursorSet( ui::CursorSetType cursor_set, - views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE; - virtual void SetScale( - float scale, - views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE; + wm::NativeCursorManagerDelegate* delegate) OVERRIDE; virtual void SetMouseEventsEnabled( bool enabled, - views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE; + wm::NativeCursorManagerDelegate* delegate) OVERRIDE; + + // The set of hosts to notify of changes in cursor state. + typedef std::set<aura::WindowTreeHost*> Hosts; + Hosts hosts_; - aura::RootWindow* root_window_; scoped_ptr<DesktopCursorLoaderUpdater> cursor_loader_updater_; scoped_ptr<ui::CursorLoader> cursor_loader_; 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 index 6ae83df66bb..e461fd9e7f2 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc @@ -6,18 +6,16 @@ #include "base/bind.h" #include "base/debug/trace_event.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/focus_client.h" #include "ui/aura/client/window_tree_client.h" -#include "ui/aura/root_window.h" #include "ui/aura/window.h" #include "ui/aura/window_observer.h" #include "ui/aura/window_property.h" #include "ui/aura/window_tree_host.h" #include "ui/base/hit_test.h" +#include "ui/base/ui_base_switches_util.h" #include "ui/compositor/layer.h" #include "ui/gfx/canvas.h" #include "ui/gfx/display.h" @@ -25,29 +23,20 @@ #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/cursor_manager.h" -#include "ui/views/corewm/focus_controller.h" -#include "ui/views/corewm/input_method_event_filter.h" -#include "ui/views/corewm/native_cursor_manager.h" -#include "ui/views/corewm/shadow_controller.h" -#include "ui/views/corewm/shadow_types.h" #include "ui/views/corewm/tooltip.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/ime/null_input_method.h" +#include "ui/views/view_constants_aura.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_event_client.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_root_window_host.h" #include "ui/views/widget/desktop_aura/desktop_screen_position_client.h" +#include "ui/views/widget/desktop_aura/desktop_window_tree_host.h" #include "ui/views/widget/drop_helper.h" #include "ui/views/widget/native_widget_aura.h" #include "ui/views/widget/root_view.h" @@ -56,8 +45,21 @@ #include "ui/views/widget/widget_aura_utils.h" #include "ui/views/widget/widget_delegate.h" #include "ui/views/widget/window_reorderer.h" +#include "ui/views/window/native_frame_view.h" +#include "ui/wm/core/compound_event_filter.h" +#include "ui/wm/core/cursor_manager.h" +#include "ui/wm/core/focus_controller.h" +#include "ui/wm/core/input_method_event_filter.h" +#include "ui/wm/core/native_cursor_manager.h" +#include "ui/wm/core/shadow_controller.h" +#include "ui/wm/core/shadow_types.h" +#include "ui/wm/core/visibility_controller.h" +#include "ui/wm/core/window_modality_controller.h" +#include "ui/wm/public/activation_client.h" +#include "ui/wm/public/drag_drop_client.h" #if defined(OS_WIN) +#include "ui/base/win/shell.h" #include "ui/gfx/win/dpi.h" #endif @@ -79,7 +81,8 @@ class DesktopNativeWidgetTopLevelHandler : public aura::WindowObserver { // becomes the parent of the child window passed in. static aura::Window* CreateParentWindow(aura::Window* child_window, const gfx::Rect& bounds, - bool full_screen) { + bool full_screen, + bool root_is_always_on_top) { // This instance will get deleted when the widget is destroyed. DesktopNativeWidgetTopLevelHandler* top_level_handler = new DesktopNativeWidgetTopLevelHandler; @@ -91,8 +94,12 @@ class DesktopNativeWidgetTopLevelHandler : public aura::WindowObserver { Widget::InitParams::TYPE_POPUP; init_params.bounds = bounds; init_params.ownership = Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET; - init_params.layer_type = ui::LAYER_NOT_DRAWN; - init_params.can_activate = full_screen; + init_params.layer_type = aura::WINDOW_LAYER_NOT_DRAWN; + init_params.activatable = full_screen ? + Widget::InitParams::ACTIVATABLE_YES : + Widget::InitParams::ACTIVATABLE_NO; + init_params.keep_on_top = root_is_always_on_top; + // This widget instance will get deleted when the window is // destroyed. top_level_handler->top_level_widget_ = new Widget(); @@ -171,10 +178,17 @@ class DesktopNativeWidgetAuraWindowTreeClient : const gfx::Rect& bounds) OVERRIDE { bool is_fullscreen = window->GetProperty(aura::client::kShowStateKey) == ui::SHOW_STATE_FULLSCREEN; - bool is_menu = window->type() == aura::client::WINDOW_TYPE_MENU; + bool is_menu = window->type() == ui::wm::WINDOW_TYPE_MENU; + if (is_fullscreen || is_menu) { + bool root_is_always_on_top = false; + internal::NativeWidgetPrivate* native_widget = + DesktopNativeWidgetAura::ForWindow(root_window_); + if (native_widget) + root_is_always_on_top = native_widget->IsAlwaysOnTop(); + return DesktopNativeWidgetTopLevelHandler::CreateParentWindow( - window, bounds, is_fullscreen); + window, bounds, is_fullscreen, root_is_always_on_top); } return root_window_; } @@ -207,15 +221,38 @@ class FocusManagerEventHandler : public ui::EventHandler { DISALLOW_COPY_AND_ASSIGN(FocusManagerEventHandler); }; +class RootWindowDestructionObserver : public aura::WindowObserver { + public: + explicit RootWindowDestructionObserver(DesktopNativeWidgetAura* parent) + : parent_(parent) {} + virtual ~RootWindowDestructionObserver() {} + + private: + // Overridden from aura::WindowObserver: + virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE { + parent_->RootWindowDestroyed(); + window->RemoveObserver(this); + delete this; + } + + DesktopNativeWidgetAura* parent_; + + DISALLOW_COPY_AND_ASSIGN(RootWindowDestructionObserver); +}; + //////////////////////////////////////////////////////////////////////////////// // DesktopNativeWidgetAura, public: +int DesktopNativeWidgetAura::cursor_reference_count_ = 0; +DesktopNativeCursorManager* DesktopNativeWidgetAura::native_cursor_manager_ = + NULL; +wm::CursorManager* DesktopNativeWidgetAura::cursor_manager_ = NULL; + DesktopNativeWidgetAura::DesktopNativeWidgetAura( internal::NativeWidgetDelegate* delegate) - : ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET), + : desktop_window_tree_host_(NULL), + ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET), close_widget_factory_(this), - can_activate_(true), - desktop_root_window_host_(NULL), content_window_container_(NULL), content_window_(new aura::Window(this)), native_widget_delegate_(delegate), @@ -223,7 +260,6 @@ DesktopNativeWidgetAura::DesktopNativeWidgetAura( restore_focus_on_activate_(false), cursor_(gfx::kNullCursor), widget_type_(Widget::InitParams::TYPE_WINDOW) { - content_window_->SetProperty(kDesktopNativeWidgetAuraKey, this); aura::client::SetFocusChangeObserver(content_window_, this); aura::client::SetActivationChangeObserver(content_window_, this); } @@ -243,48 +279,50 @@ DesktopNativeWidgetAura* DesktopNativeWidgetAura::ForWindow( void DesktopNativeWidgetAura::OnHostClosed() { // Don't invoke Widget::OnNativeWidgetDestroying(), its done by - // DesktopRootWindowHost. + // DesktopWindowTreeHost. // The WindowModalityController is at the front of the event pretarget // handler list. We destroy it first to preserve order symantics. if (window_modality_controller_) window_modality_controller_.reset(); - // Make sure we don't have capture. Otherwise CaptureController and RootWindow - // are left referencing a deleted Window. + // Make sure we don't have capture. Otherwise CaptureController and + // WindowEventDispatcher are left referencing a deleted Window. { aura::Window* capture_window = capture_client_->GetCaptureWindow(); - if (capture_window && root_window_->window()->Contains(capture_window)) + if (capture_window && host_->window()->Contains(capture_window)) capture_window->ReleaseCapture(); } - // DesktopRootWindowHost owns the ActivationController which ShadowController + // DesktopWindowTreeHost owns the ActivationController which ShadowController // references. Make sure we destroy ShadowController early on. shadow_controller_.reset(); tooltip_manager_.reset(); - root_window_->window()->RemovePreTargetHandler(tooltip_controller_.get()); - aura::client::SetTooltipClient(root_window_->window(), NULL); - tooltip_controller_.reset(); + if (tooltip_controller_.get()) { + host_->window()->RemovePreTargetHandler(tooltip_controller_.get()); + aura::client::SetTooltipClient(host_->window(), NULL); + tooltip_controller_.reset(); + } root_window_event_filter_->RemoveHandler(input_method_event_filter_.get()); - window_tree_client_.reset(); // Uses root_window_ at destruction. + window_tree_client_.reset(); // Uses host_->dispatcher() at destruction. - capture_client_.reset(); // Uses root_window_ at destruction. + capture_client_.reset(); // Uses host_->dispatcher() at destruction. // FocusController uses |content_window_|. Destroy it now so that we don't // have to worry about the possibility of FocusController attempting to use // |content_window_| after it's been destroyed but before all child windows // have been destroyed. - root_window_->window()->RemovePreTargetHandler(focus_client_.get()); - aura::client::SetFocusClient(root_window_->window(), NULL); - aura::client::SetActivationClient(root_window_->window(), NULL); + host_->window()->RemovePreTargetHandler(focus_client_.get()); + aura::client::SetFocusClient(host_->window(), NULL); + aura::client::SetActivationClient(host_->window(), NULL); focus_client_.reset(); - root_window_->RemoveRootWindowObserver(this); - root_window_.reset(); // Uses input_method_event_filter_ at destruction. - // RootWindow owns |desktop_root_window_host_|. - desktop_root_window_host_ = NULL; + host_->RemoveObserver(this); + host_.reset(); // Uses input_method_event_filter_ at destruction. + // WindowEventDispatcher owns |desktop_window_tree_host_|. + desktop_window_tree_host_ = NULL; content_window_ = NULL; native_widget_delegate_->OnNativeWidgetDestroyed(); @@ -292,30 +330,36 @@ void DesktopNativeWidgetAura::OnHostClosed() { delete this; } -void DesktopNativeWidgetAura::OnDesktopRootWindowHostDestroyed( - aura::RootWindow* root) { - // |root_window_| is still valid, but DesktopRootWindowHost is nearly - // destroyed. Do cleanup here of members DesktopRootWindowHost may also use. - aura::client::SetDispatcherClient(root->window(), NULL); +void DesktopNativeWidgetAura::OnDesktopWindowTreeHostDestroyed( + aura::WindowTreeHost* host) { + // |dispatcher_| is still valid, but DesktopWindowTreeHost is nearly + // destroyed. Do cleanup here of members DesktopWindowTreeHost may also use. + aura::client::SetDispatcherClient(host->window(), NULL); dispatcher_client_.reset(); - aura::client::SetCursorClient(root->window(), NULL); - cursor_client_.reset(); + // We explicitly do NOT clear the cursor client property. Since the cursor + // manager is a singleton, it can outlive any window hierarchy, and it's + // important that objects attached to this destroying window hierarchy have + // an opportunity to deregister their observers from the cursor manager. + // They may want to do this when they are notified that they're being + // removed from the window hierarchy, which happens soon after this + // function when DesktopWindowTreeHost* calls DestroyDispatcher(). + native_cursor_manager_->RemoveHost(host); - aura::client::SetScreenPositionClient(root->window(), NULL); + aura::client::SetScreenPositionClient(host->window(), NULL); position_client_.reset(); - aura::client::SetDragDropClient(root->window(), NULL); + aura::client::SetDragDropClient(host->window(), NULL); drag_drop_client_.reset(); - aura::client::SetEventClient(root->window(), NULL); + aura::client::SetEventClient(host->window(), NULL); event_client_.reset(); } void DesktopNativeWidgetAura::HandleActivationChanged(bool active) { native_widget_delegate_->OnNativeWidgetActivationChanged(active); aura::client::ActivationClient* activation_client = - aura::client::GetActivationClient(root_window_->window()); + aura::client::GetActivationClient(host_->window()); if (!activation_client) return; if (active) { @@ -361,142 +405,155 @@ void DesktopNativeWidgetAura::InitNativeWidget( } content_window_->SetType(GetAuraWindowTypeForWidgetType(params.type)); content_window_->Init(params.layer_type); - corewm::SetShadowType(content_window_, corewm::SHADOW_TYPE_NONE); + wm::SetShadowType(content_window_, wm::SHADOW_TYPE_NONE); content_window_container_ = new aura::Window(NULL); - content_window_container_->Init(ui::LAYER_NOT_DRAWN); + content_window_container_->Init(aura::WINDOW_LAYER_NOT_DRAWN); content_window_container_->Show(); content_window_container_->AddChild(content_window_); - desktop_root_window_host_ = params.desktop_root_window_host ? - params.desktop_root_window_host : - DesktopRootWindowHost::Create(native_widget_delegate_, this); - aura::RootWindow::CreateParams rw_params(params.bounds); - desktop_root_window_host_->Init(content_window_, params, &rw_params); + desktop_window_tree_host_ = params.desktop_window_tree_host ? + params.desktop_window_tree_host : + DesktopWindowTreeHost::Create(native_widget_delegate_, this); + host_.reset(desktop_window_tree_host_->AsWindowTreeHost()); + desktop_window_tree_host_->Init(content_window_, params); + + // Mark this window as Desktop root window. + host_->window()->SetProperty(views::kDesktopRootWindow, true); + + host_->InitHost(); + host_->window()->AddChild(content_window_container_); + host_->window()->SetProperty(kDesktopNativeWidgetAuraKey, this); - root_window_.reset(new aura::RootWindow(rw_params)); - root_window_->Init(); - root_window_->window()->AddChild(content_window_container_); + host_->window()->AddObserver(new RootWindowDestructionObserver(this)); // The WindowsModalityController event filter should be at the head of the // pre target handlers list. This ensures that it handles input events first // when modal windows are at the top of the Zorder. if (widget_type_ == Widget::InitParams::TYPE_WINDOW) window_modality_controller_.reset( - new views::corewm::WindowModalityController(root_window_->window())); + new wm::WindowModalityController(host_->window())); // |root_window_event_filter_| must be created before - // OnRootWindowHostCreated() is invoked. + // OnWindowTreeHostCreated() is invoked. // 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_window_->window()->SetEventFilter(root_window_event_filter_); + // No event filter for aura::Env. Create CompoundEventFilter per + // WindowEventDispatcher. + root_window_event_filter_.reset(new wm::CompoundEventFilter); + host_->window()->AddPreTargetHandler(root_window_event_filter_.get()); + + // The host's dispatcher must be added to |native_cursor_manager_| before + // OnNativeWidgetCreated() is called. + cursor_reference_count_++; + if (!native_cursor_manager_) { + native_cursor_manager_ = new DesktopNativeCursorManager( + DesktopCursorLoaderUpdater::Create()); + } + if (!cursor_manager_) { + cursor_manager_ = new wm::CursorManager( + scoped_ptr<wm::NativeCursorManager>(native_cursor_manager_)); + } + native_cursor_manager_->AddHost(host()); + aura::client::SetCursorClient(host_->window(), cursor_manager_); - desktop_root_window_host_->OnRootWindowCreated(root_window_.get(), params); + desktop_window_tree_host_->OnNativeWidgetCreated(params); UpdateWindowTransparency(); - capture_client_.reset(new DesktopCaptureClient(root_window_->window())); + capture_client_.reset(new DesktopCaptureClient(host_->window())); - corewm::FocusController* focus_controller = - new corewm::FocusController(new DesktopFocusRules(content_window_)); + wm::FocusController* focus_controller = + new wm::FocusController(new DesktopFocusRules(content_window_)); focus_client_.reset(focus_controller); - aura::client::SetFocusClient(root_window_->window(), focus_controller); - aura::client::SetActivationClient(root_window_->window(), focus_controller); - root_window_->window()->AddPreTargetHandler(focus_controller); + aura::client::SetFocusClient(host_->window(), focus_controller); + aura::client::SetActivationClient(host_->window(), focus_controller); + host_->window()->AddPreTargetHandler(focus_controller); dispatcher_client_.reset(new DesktopDispatcherClient); - aura::client::SetDispatcherClient(root_window_->window(), + aura::client::SetDispatcherClient(host_->window(), dispatcher_client_.get()); - DesktopNativeCursorManager* desktop_native_cursor_manager = - new views::DesktopNativeCursorManager( - root_window_.get(), - DesktopCursorLoaderUpdater::Create()); - cursor_client_.reset( - new views::corewm::CursorManager( - scoped_ptr<corewm::NativeCursorManager>( - desktop_native_cursor_manager))); - aura::client::SetCursorClient(root_window_->window(), cursor_client_.get()); - - position_client_.reset(new DesktopScreenPositionClient()); - aura::client::SetScreenPositionClient(root_window_->window(), - position_client_.get()); + position_client_.reset(new DesktopScreenPositionClient(host_->window())); InstallInputMethodEventFilter(); - drag_drop_client_ = desktop_root_window_host_->CreateDragDropClient( - desktop_native_cursor_manager); - aura::client::SetDragDropClient(root_window_->window(), + drag_drop_client_ = desktop_window_tree_host_->CreateDragDropClient( + native_cursor_manager_); + aura::client::SetDragDropClient(host_->window(), drag_drop_client_.get()); static_cast<aura::client::FocusClient*>(focus_client_.get())-> FocusWindow(content_window_); - OnRootWindowHostResized(root_window_.get()); + OnHostResized(host()); - root_window_->AddRootWindowObserver(this); + host_->AddObserver(this); window_tree_client_.reset( - new DesktopNativeWidgetAuraWindowTreeClient(root_window_->window())); - drop_helper_.reset(new DropHelper( - static_cast<internal::RootView*>(GetWidget()->GetRootView()))); + new DesktopNativeWidgetAuraWindowTreeClient(host_->window())); + drop_helper_.reset(new DropHelper(GetWidget()->GetRootView())); aura::client::SetDragDropDelegate(content_window_, this); - tooltip_manager_.reset(new TooltipManagerAura(GetWidget())); - - tooltip_controller_.reset( - new corewm::TooltipController( - desktop_root_window_host_->CreateTooltip())); - aura::client::SetTooltipClient(root_window_->window(), - tooltip_controller_.get()); - root_window_->window()->AddPreTargetHandler(tooltip_controller_.get()); + if (params.type != Widget::InitParams::TYPE_TOOLTIP) { + tooltip_manager_.reset(new TooltipManagerAura(GetWidget())); + tooltip_controller_.reset( + new corewm::TooltipController( + desktop_window_tree_host_->CreateTooltip())); + aura::client::SetTooltipClient(host_->window(), + tooltip_controller_.get()); + host_->window()->AddPreTargetHandler(tooltip_controller_.get()); + } if (params.opacity == Widget::InitParams::TRANSLUCENT_WINDOW) { - visibility_controller_.reset(new views::corewm::VisibilityController); - aura::client::SetVisibilityClient(root_window_->window(), + visibility_controller_.reset(new wm::VisibilityController); + aura::client::SetVisibilityClient(host_->window(), visibility_controller_.get()); - views::corewm::SetChildWindowVisibilityChangesAnimated( - root_window_->window()); - views::corewm::SetChildWindowVisibilityChangesAnimated( + wm::SetChildWindowVisibilityChangesAnimated(host_->window()); + wm::SetChildWindowVisibilityChangesAnimated( content_window_container_); } if (params.type == Widget::InitParams::TYPE_WINDOW) { focus_manager_event_handler_.reset(new FocusManagerEventHandler(this)); - root_window_->window()->AddPreTargetHandler( - focus_manager_event_handler_.get()); + host_->window()->AddPreTargetHandler(focus_manager_event_handler_.get()); } event_client_.reset(new DesktopEventClient); - aura::client::SetEventClient(root_window_->window(), event_client_.get()); + aura::client::SetEventClient(host_->window(), event_client_.get()); aura::client::GetFocusClient(content_window_)->FocusWindow(content_window_); aura::client::SetActivationDelegate(content_window_, this); - shadow_controller_.reset( - new corewm::ShadowController( - aura::client::GetActivationClient(root_window_->window()))); + shadow_controller_.reset(new wm::ShadowController( + aura::client::GetActivationClient(host_->window()))); + + content_window_->SetProperty(aura::client::kCanMaximizeKey, + GetWidget()->widget_delegate()->CanMaximize()); + content_window_->SetProperty(aura::client::kCanResizeKey, + GetWidget()->widget_delegate()->CanResize()); window_reorderer_.reset(new WindowReorderer(content_window_, GetWidget()->GetRootView())); } NonClientFrameView* DesktopNativeWidgetAura::CreateNonClientFrameView() { - return desktop_root_window_host_->CreateNonClientFrameView(); + return ShouldUseNativeFrame() ? new NativeFrameView(GetWidget()) : NULL; } bool DesktopNativeWidgetAura::ShouldUseNativeFrame() const { - return desktop_root_window_host_->ShouldUseNativeFrame(); + return desktop_window_tree_host_->ShouldUseNativeFrame(); +} + +bool DesktopNativeWidgetAura::ShouldWindowContentsBeTransparent() const { + return desktop_window_tree_host_->ShouldWindowContentsBeTransparent(); } void DesktopNativeWidgetAura::FrameTypeChanged() { - desktop_root_window_host_->FrameTypeChanged(); + desktop_window_tree_host_->FrameTypeChanged(); UpdateWindowTransparency(); } @@ -538,6 +595,8 @@ void DesktopNativeWidgetAura::ReorderNativeViews() { } void DesktopNativeWidgetAura::ViewRemoved(View* view) { + DCHECK(drop_helper_.get() != NULL); + drop_helper_->ResetTargetViewIfEquals(view); } void DesktopNativeWidgetAura::SetNativeWindowProperty(const char* name, @@ -571,10 +630,13 @@ void DesktopNativeWidgetAura::ReleaseCapture() { bool DesktopNativeWidgetAura::HasCapture() const { return content_window_ && content_window_->HasCapture() && - desktop_root_window_host_->HasCapture(); + desktop_window_tree_host_->HasCapture(); } InputMethod* DesktopNativeWidgetAura::CreateInputMethod() { + if (switches::IsTextInputFocusManagerEnabled()) + return new NullInputMethod(); + ui::InputMethod* host = input_method_event_filter_->input_method(); return new InputMethodBridge(this, host, false); } @@ -584,50 +646,54 @@ internal::InputMethodDelegate* return this; } +ui::InputMethod* DesktopNativeWidgetAura::GetHostInputMethod() { + return input_method_event_filter_->input_method(); +} + void DesktopNativeWidgetAura::CenterWindow(const gfx::Size& size) { if (content_window_) - desktop_root_window_host_->CenterWindow(size); + desktop_window_tree_host_->CenterWindow(size); } void DesktopNativeWidgetAura::GetWindowPlacement( gfx::Rect* bounds, ui::WindowShowState* maximized) const { if (content_window_) - desktop_root_window_host_->GetWindowPlacement(bounds, maximized); + desktop_window_tree_host_->GetWindowPlacement(bounds, maximized); } -bool DesktopNativeWidgetAura::SetWindowTitle(const string16& title) { +bool DesktopNativeWidgetAura::SetWindowTitle(const base::string16& title) { if (!content_window_) return false; - return desktop_root_window_host_->SetWindowTitle(title); + return desktop_window_tree_host_->SetWindowTitle(title); } void DesktopNativeWidgetAura::SetWindowIcons(const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon) { if (content_window_) - desktop_root_window_host_->SetWindowIcons(window_icon, app_icon); + desktop_window_tree_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. We only support window modal // dialogs on the same lines as non AURA. - desktop_root_window_host_->InitModalType(modal_type); + desktop_window_tree_host_->InitModalType(modal_type); } gfx::Rect DesktopNativeWidgetAura::GetWindowBoundsInScreen() const { return content_window_ ? - desktop_root_window_host_->GetWindowBoundsInScreen() : gfx::Rect(); + desktop_window_tree_host_->GetWindowBoundsInScreen() : gfx::Rect(); } gfx::Rect DesktopNativeWidgetAura::GetClientAreaBoundsInScreen() const { return content_window_ ? - desktop_root_window_host_->GetClientAreaBoundsInScreen() : gfx::Rect(); + desktop_window_tree_host_->GetClientAreaBoundsInScreen() : gfx::Rect(); } gfx::Rect DesktopNativeWidgetAura::GetRestoredBounds() const { return content_window_ ? - desktop_root_window_host_->GetRestoredBounds() : gfx::Rect(); + desktop_window_tree_host_->GetRestoredBounds() : gfx::Rect(); } void DesktopNativeWidgetAura::SetBounds(const gfx::Rect& bounds) { @@ -636,9 +702,9 @@ void DesktopNativeWidgetAura::SetBounds(const gfx::Rect& bounds) { // TODO(ananta) // This code by default scales the bounds rectangle by 1. // We could probably get rid of this and similar logic from - // the DesktopNativeWidgetAura::OnRootWindowHostResized function. + // the DesktopNativeWidgetAura::OnWindowTreeHostResized function. float scale = 1; - aura::Window* root = root_window_->window(); + aura::Window* root = host_->window(); if (root) { scale = gfx::Screen::GetScreenFor(root)-> GetDisplayNearestWindow(root).device_scale_factor(); @@ -646,12 +712,12 @@ void DesktopNativeWidgetAura::SetBounds(const gfx::Rect& bounds) { 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); + desktop_window_tree_host_->AsWindowTreeHost()->SetBounds(bounds_in_pixels); } void DesktopNativeWidgetAura::SetSize(const gfx::Size& size) { if (content_window_) - desktop_root_window_host_->SetSize(size); + desktop_window_tree_host_->SetSize(size); } void DesktopNativeWidgetAura::StackAbove(gfx::NativeView native_view) { @@ -659,7 +725,7 @@ void DesktopNativeWidgetAura::StackAbove(gfx::NativeView native_view) { void DesktopNativeWidgetAura::StackAtTop() { if (content_window_) - desktop_root_window_host_->StackAtTop(); + desktop_window_tree_host_->StackAtTop(); } void DesktopNativeWidgetAura::StackBelow(gfx::NativeView native_view) { @@ -667,7 +733,7 @@ void DesktopNativeWidgetAura::StackBelow(gfx::NativeView native_view) { void DesktopNativeWidgetAura::SetShape(gfx::NativeRegion shape) { if (content_window_) - desktop_root_window_host_->SetShape(shape); + desktop_window_tree_host_->SetShape(shape); } void DesktopNativeWidgetAura::Close() { @@ -677,25 +743,25 @@ void DesktopNativeWidgetAura::Close() { content_window_->SuppressPaint(); content_window_->Hide(); - desktop_root_window_host_->Close(); + desktop_window_tree_host_->Close(); } void DesktopNativeWidgetAura::CloseNow() { if (content_window_) - desktop_root_window_host_->CloseNow(); + desktop_window_tree_host_->CloseNow(); } void DesktopNativeWidgetAura::Show() { if (!content_window_) return; - desktop_root_window_host_->AsRootWindowHost()->Show(); + desktop_window_tree_host_->AsWindowTreeHost()->Show(); content_window_->Show(); } void DesktopNativeWidgetAura::Hide() { if (!content_window_) return; - desktop_root_window_host_->AsRootWindowHost()->Hide(); + desktop_window_tree_host_->AsWindowTreeHost()->Hide(); content_window_->Hide(); } @@ -703,79 +769,84 @@ void DesktopNativeWidgetAura::ShowMaximizedWithBounds( const gfx::Rect& restored_bounds) { if (!content_window_) return; - desktop_root_window_host_->ShowMaximizedWithBounds(restored_bounds); + desktop_window_tree_host_->ShowMaximizedWithBounds(restored_bounds); content_window_->Show(); } void DesktopNativeWidgetAura::ShowWithWindowState(ui::WindowShowState state) { if (!content_window_) return; - desktop_root_window_host_->ShowWindowWithState(state); + desktop_window_tree_host_->ShowWindowWithState(state); content_window_->Show(); } bool DesktopNativeWidgetAura::IsVisible() const { - return content_window_ && desktop_root_window_host_->IsVisible(); + return content_window_ && desktop_window_tree_host_->IsVisible(); } void DesktopNativeWidgetAura::Activate() { if (content_window_) - desktop_root_window_host_->Activate(); + desktop_window_tree_host_->Activate(); } void DesktopNativeWidgetAura::Deactivate() { if (content_window_) - desktop_root_window_host_->Deactivate(); + desktop_window_tree_host_->Deactivate(); } bool DesktopNativeWidgetAura::IsActive() const { - return content_window_ && desktop_root_window_host_->IsActive(); + return content_window_ && desktop_window_tree_host_->IsActive(); } void DesktopNativeWidgetAura::SetAlwaysOnTop(bool always_on_top) { if (content_window_) - desktop_root_window_host_->SetAlwaysOnTop(always_on_top); + desktop_window_tree_host_->SetAlwaysOnTop(always_on_top); } bool DesktopNativeWidgetAura::IsAlwaysOnTop() const { - return content_window_ && desktop_root_window_host_->IsAlwaysOnTop(); + return content_window_ && desktop_window_tree_host_->IsAlwaysOnTop(); +} + +void DesktopNativeWidgetAura::SetVisibleOnAllWorkspaces(bool always_visible) { + if (content_window_) + desktop_window_tree_host_->SetVisibleOnAllWorkspaces(always_visible); } void DesktopNativeWidgetAura::Maximize() { if (content_window_) - desktop_root_window_host_->Maximize(); + desktop_window_tree_host_->Maximize(); } void DesktopNativeWidgetAura::Minimize() { if (content_window_) - desktop_root_window_host_->Minimize(); + desktop_window_tree_host_->Minimize(); } bool DesktopNativeWidgetAura::IsMaximized() const { - return content_window_ && desktop_root_window_host_->IsMaximized(); + return content_window_ && desktop_window_tree_host_->IsMaximized(); } bool DesktopNativeWidgetAura::IsMinimized() const { - return content_window_ && desktop_root_window_host_->IsMinimized(); + return content_window_ && desktop_window_tree_host_->IsMinimized(); } void DesktopNativeWidgetAura::Restore() { if (content_window_) - desktop_root_window_host_->Restore(); + desktop_window_tree_host_->Restore(); } void DesktopNativeWidgetAura::SetFullscreen(bool fullscreen) { if (content_window_) - desktop_root_window_host_->SetFullscreen(fullscreen); + desktop_window_tree_host_->SetFullscreen(fullscreen); } bool DesktopNativeWidgetAura::IsFullscreen() const { - return content_window_ && desktop_root_window_host_->IsFullscreen(); + return content_window_ && desktop_window_tree_host_->IsFullscreen(); } void DesktopNativeWidgetAura::SetOpacity(unsigned char opacity) { if (content_window_) - desktop_root_window_host_->SetOpacity(opacity); + desktop_window_tree_host_->SetOpacity(opacity); } void DesktopNativeWidgetAura::SetUseDragFrame(bool use_drag_frame) { @@ -783,7 +854,7 @@ void DesktopNativeWidgetAura::SetUseDragFrame(bool use_drag_frame) { void DesktopNativeWidgetAura::FlashFrame(bool flash_frame) { if (content_window_) - desktop_root_window_host_->FlashFrame(flash_frame); + desktop_window_tree_host_->FlashFrame(flash_frame); } void DesktopNativeWidgetAura::RunShellDrag( @@ -803,7 +874,7 @@ void DesktopNativeWidgetAura::SchedulePaintInRect(const gfx::Rect& rect) { void DesktopNativeWidgetAura::SetCursor(gfx::NativeCursor cursor) { cursor_ = cursor; aura::client::CursorClient* cursor_client = - aura::client::GetCursorClient(root_window_->window()); + aura::client::GetCursorClient(host_->window()); if (cursor_client) cursor_client->SetCursor(cursor); } @@ -812,12 +883,12 @@ bool DesktopNativeWidgetAura::IsMouseEventsEnabled() const { if (!content_window_) return false; aura::client::CursorClient* cursor_client = - aura::client::GetCursorClient(root_window_->window()); + aura::client::GetCursorClient(host_->window()); return cursor_client ? cursor_client->IsMouseEventsEnabled() : true; } void DesktopNativeWidgetAura::ClearNativeFocus() { - desktop_root_window_host_->ClearNativeFocus(); + desktop_window_tree_host_->ClearNativeFocus(); if (ShouldActivate()) { aura::client::GetFocusClient(content_window_)-> @@ -826,8 +897,8 @@ void DesktopNativeWidgetAura::ClearNativeFocus() { } gfx::Rect DesktopNativeWidgetAura::GetWorkAreaBoundsInScreen() const { - return desktop_root_window_host_ ? - desktop_root_window_host_->GetWorkAreaBoundsInScreen() : gfx::Rect(); + return desktop_window_tree_host_ ? + desktop_window_tree_host_->GetWorkAreaBoundsInScreen() : gfx::Rect(); } Widget::MoveLoopResult DesktopNativeWidgetAura::RunMoveLoop( @@ -836,28 +907,37 @@ Widget::MoveLoopResult DesktopNativeWidgetAura::RunMoveLoop( Widget::MoveLoopEscapeBehavior escape_behavior) { if (!content_window_) return Widget::MOVE_LOOP_CANCELED; - return desktop_root_window_host_->RunMoveLoop(drag_offset, source, + return desktop_window_tree_host_->RunMoveLoop(drag_offset, source, escape_behavior); } void DesktopNativeWidgetAura::EndMoveLoop() { if (content_window_) - desktop_root_window_host_->EndMoveLoop(); + desktop_window_tree_host_->EndMoveLoop(); } void DesktopNativeWidgetAura::SetVisibilityChangedAnimationsEnabled( bool value) { if (content_window_) - desktop_root_window_host_->SetVisibilityChangedAnimationsEnabled(value); + desktop_window_tree_host_->SetVisibilityChangedAnimationsEnabled(value); } ui::NativeTheme* DesktopNativeWidgetAura::GetNativeTheme() const { - return DesktopRootWindowHost::GetNativeTheme(content_window_); + return DesktopWindowTreeHost::GetNativeTheme(content_window_); } void DesktopNativeWidgetAura::OnRootViewLayout() const { if (content_window_) - desktop_root_window_host_->OnRootViewLayout(); + desktop_window_tree_host_->OnRootViewLayout(); +} + +bool DesktopNativeWidgetAura::IsTranslucentWindowOpacitySupported() const { + return content_window_ && + desktop_window_tree_host_->IsTranslucentWindowOpacitySupported(); +} + +void DesktopNativeWidgetAura::RepostNativeEvent(gfx::NativeEvent native_event) { + OnEvent(native_event); } //////////////////////////////////////////////////////////////////////////////// @@ -904,13 +984,13 @@ void DesktopNativeWidgetAura::OnDeviceScaleFactorChanged( float device_scale_factor) { } -void DesktopNativeWidgetAura::OnWindowDestroying() { +void DesktopNativeWidgetAura::OnWindowDestroying(aura::Window* window) { // Cleanup happens in OnHostClosed(). } -void DesktopNativeWidgetAura::OnWindowDestroyed() { +void DesktopNativeWidgetAura::OnWindowDestroyed(aura::Window* window) { // Cleanup happens in OnHostClosed(). We own |content_window_| (indirectly by - // way of |root_window_|) so there should be no need to do any processing + // way of |dispatcher_|) so there should be no need to do any processing // here. } @@ -925,9 +1005,6 @@ void DesktopNativeWidgetAura::GetHitTestMask(gfx::Path* mask) const { native_widget_delegate_->GetHitTestMask(mask); } -void DesktopNativeWidgetAura::DidRecreateLayer(ui::Layer* old_layer, - ui::Layer* new_layer) {} - //////////////////////////////////////////////////////////////////////////////// // DesktopNativeWidgetAura, ui::EventHandler implementation: @@ -978,10 +1055,6 @@ void DesktopNativeWidgetAura::OnScrollEvent(ui::ScrollEvent* event) { } } -void DesktopNativeWidgetAura::OnTouchEvent(ui::TouchEvent* event) { - native_widget_delegate_->OnTouchEvent(event); -} - void DesktopNativeWidgetAura::OnGestureEvent(ui::GestureEvent* event) { native_widget_delegate_->OnGestureEvent(event); } @@ -990,7 +1063,7 @@ void DesktopNativeWidgetAura::OnGestureEvent(ui::GestureEvent* event) { // DesktopNativeWidgetAura, aura::client::ActivationDelegate implementation: bool DesktopNativeWidgetAura::ShouldActivate() const { - return can_activate_ && native_widget_delegate_->CanActivate(); + return native_widget_delegate_->CanActivate(); } //////////////////////////////////////////////////////////////////////////////// @@ -1000,10 +1073,6 @@ bool DesktopNativeWidgetAura::ShouldActivate() const { void DesktopNativeWidgetAura::OnWindowActivated(aura::Window* gained_active, aura::Window* lost_active) { DCHECK(content_window_ == gained_active || content_window_ == lost_active); - if ((content_window_ == gained_active || content_window_ == lost_active) && - IsVisible() && GetWidget()->non_client_view()) { - GetWidget()->non_client_view()->SchedulePaint(); - } if (gained_active == content_window_ && restore_focus_on_activate_) { restore_focus_on_activate_ = false; GetWidget()->GetFocusManager()->RestoreFocusedView(); @@ -1021,19 +1090,18 @@ void DesktopNativeWidgetAura::OnWindowActivated(aura::Window* gained_active, void DesktopNativeWidgetAura::OnWindowFocused(aura::Window* gained_focus, aura::Window* lost_focus) { if (content_window_ == gained_focus) { - desktop_root_window_host_->OnNativeWidgetFocus(); + desktop_window_tree_host_->OnNativeWidgetFocus(); native_widget_delegate_->OnNativeFocus(lost_focus); // If focus is moving from a descendant Window to |content_window_| then - // native activation hasn't changed. We still need to inform the InputMethod - // we've been focused though. + // native activation hasn't changed. Still, the InputMethod must be informed + // of the Window focus change. InputMethod* input_method = GetWidget()->GetInputMethod(); if (input_method) input_method->OnFocus(); } else if (content_window_ == lost_focus) { - desktop_root_window_host_->OnNativeWidgetBlur(); - native_widget_delegate_->OnNativeBlur( - aura::client::GetFocusClient(content_window_)->GetFocusedWindow()); + desktop_window_tree_host_->OnNativeWidgetBlur(); + native_widget_delegate_->OnNativeBlur(gained_focus); } } @@ -1078,22 +1146,21 @@ int DesktopNativeWidgetAura::OnPerformDrop(const ui::DropTargetEvent& event) { } //////////////////////////////////////////////////////////////////////////////// -// DesktopNativeWidgetAura, aura::RootWindowObserver implementation: +// DesktopNativeWidgetAura, aura::WindowTreeHostObserver implementation: -void DesktopNativeWidgetAura::OnRootWindowHostCloseRequested( - const aura::RootWindow* root) { - Close(); +void DesktopNativeWidgetAura::OnHostCloseRequested( + const aura::WindowTreeHost* host) { + GetWidget()->Close(); } -void DesktopNativeWidgetAura::OnRootWindowHostResized( - const aura::RootWindow* root) { +void DesktopNativeWidgetAura::OnHostResized(const aura::WindowTreeHost* host) { // Don't update the bounds of the child layers when animating closed. If we // did it would force a paint, which we don't want. We don't want the paint // as we can't assume any of the children are valid. - if (desktop_root_window_host_->IsAnimatingClosed()) + if (desktop_window_tree_host_->IsAnimatingClosed()) return; - gfx::Rect new_bounds = gfx::Rect(root->window()->bounds().size()); + gfx::Rect new_bounds = gfx::Rect(host->window()->bounds().size()); content_window_->SetBounds(new_bounds); // Can be NULL at start. if (content_window_container_) @@ -1101,34 +1168,45 @@ void DesktopNativeWidgetAura::OnRootWindowHostResized( native_widget_delegate_->OnNativeWidgetSizeChanged(new_bounds.size()); } -void DesktopNativeWidgetAura::OnRootWindowHostMoved( - const aura::RootWindow* root, - const gfx::Point& new_origin) { - TRACE_EVENT1("views", "DesktopNativeWidgetAura::OnRootWindowHostMoved", +void DesktopNativeWidgetAura::OnHostMoved(const aura::WindowTreeHost* host, + const gfx::Point& new_origin) { + TRACE_EVENT1("views", "DesktopNativeWidgetAura::OnHostMoved", "new_origin", new_origin.ToString()); native_widget_delegate_->OnNativeWidgetMove(); } //////////////////////////////////////////////////////////////////////////////// -// DesktopNativeWidgetAura, NativeWidget implementation: - -ui::EventHandler* DesktopNativeWidgetAura::GetEventHandler() { - return this; -} +// DesktopNativeWidgetAura, private: void DesktopNativeWidgetAura::InstallInputMethodEventFilter() { DCHECK(!input_method_event_filter_.get()); - input_method_event_filter_.reset(new corewm::InputMethodEventFilter( - root_window_->host()->GetAcceleratedWidget())); + input_method_event_filter_.reset(new wm::InputMethodEventFilter( + host_->GetAcceleratedWidget())); input_method_event_filter_->SetInputMethodPropertyInRootWindow( - root_window_->window()); + host_->window()); root_window_event_filter_->AddHandler(input_method_event_filter_.get()); } void DesktopNativeWidgetAura::UpdateWindowTransparency() { - content_window_->SetTransparent(ShouldUseNativeFrame()); + content_window_->SetTransparent( + desktop_window_tree_host_->ShouldWindowContentsBeTransparent()); + // Regardless of transparency or not, this root content window will always + // fill its bounds completely, so set this flag to true to avoid an + // unecessary clear before update. + content_window_->SetFillsBoundsCompletely(true); +} + +void DesktopNativeWidgetAura::RootWindowDestroyed() { + cursor_reference_count_--; + if (cursor_reference_count_ == 0) { + // We are the last DesktopNativeWidgetAura instance, and we are responsible + // for cleaning up |cursor_manager_|. + delete cursor_manager_; + native_cursor_manager_ = NULL; + cursor_manager_ = NULL; + } } } // 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 index 639210f3f4f..16a56d951bc 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h @@ -6,18 +6,20 @@ #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/aura/window_tree_host_observer.h" #include "ui/base/cursor/cursor.h" #include "ui/views/ime/input_method_delegate.h" #include "ui/views/widget/native_widget_private.h" +#include "ui/wm/core/compound_event_filter.h" +#include "ui/wm/public/activation_change_observer.h" +#include "ui/wm/public/activation_delegate.h" +#include "ui/wm/public/drag_drop_delegate.h" namespace aura { -class RootWindow; +class WindowEventDispatcher; +class WindowTreeHost; namespace client { class DragDropClient; class FocusClient; @@ -26,23 +28,25 @@ class WindowTreeClient; } } -namespace views { - -namespace corewm { +namespace wm { class CompoundEventFilter; class CursorManager; class FocusController; class InputMethodEventFilter; class ShadowController; -class TooltipController; class VisibilityController; class WindowModalityController; } +namespace views { +namespace corewm { +class TooltipController; +} class DesktopCaptureClient; class DesktopDispatcherClient; class DesktopEventClient; -class DesktopRootWindowHost; +class DesktopNativeCursorManager; +class DesktopWindowTreeHost; class DropHelper; class FocusManagerEventHandler; class TooltipManagerAura; @@ -56,31 +60,34 @@ class VIEWS_EXPORT DesktopNativeWidgetAura public aura::client::FocusChangeObserver, public views::internal::InputMethodDelegate, public aura::client::DragDropDelegate, - public aura::RootWindowObserver { + public aura::WindowTreeHostObserver { public: explicit DesktopNativeWidgetAura(internal::NativeWidgetDelegate* delegate); virtual ~DesktopNativeWidgetAura(); - // Maps from window to DesktopNativeWidgetAura. + // Maps from window to DesktopNativeWidgetAura. |window| must be a root + // window. static DesktopNativeWidgetAura* ForWindow(aura::Window* window); - // Called by our DesktopRootWindowHost after it has deleted native resources; + // Called by our DesktopWindowTreeHost after it has deleted native resources; // this is the signal that we should start our shutdown. virtual void OnHostClosed(); - // Called from ~DesktopRootWindowHost. This takes the RootWindow as by the - // time we get here |root_window_| is NULL. - virtual void OnDesktopRootWindowHostDestroyed(aura::RootWindow* root); + // TODO(beng): remove this method and replace with an implementation of + // WindowDestroying() that takes the window being destroyed. + // Called from ~DesktopWindowTreeHost. This takes the WindowEventDispatcher + // as by the time we get here |dispatcher_| is NULL. + virtual void OnDesktopWindowTreeHostDestroyed(aura::WindowTreeHost* host); - corewm::InputMethodEventFilter* input_method_event_filter() { + wm::InputMethodEventFilter* input_method_event_filter() { return input_method_event_filter_.get(); } - corewm::CompoundEventFilter* root_window_event_filter() { - return root_window_event_filter_; + wm::CompoundEventFilter* root_window_event_filter() { + return root_window_event_filter_.get(); + } + aura::WindowTreeHost* host() { + return host_.get(); } - - // Overridden from NativeWidget: - virtual ui::EventHandler* GetEventHandler() OVERRIDE; // Ensures that the correct window is activated/deactivated based on whether // we are being activated/deactivated. @@ -91,6 +98,7 @@ class VIEWS_EXPORT DesktopNativeWidgetAura virtual void InitNativeWidget(const Widget::InitParams& params) OVERRIDE; virtual NonClientFrameView* CreateNonClientFrameView() OVERRIDE; virtual bool ShouldUseNativeFrame() const OVERRIDE; + virtual bool ShouldWindowContentsBeTransparent() const OVERRIDE; virtual void FrameTypeChanged() OVERRIDE; virtual Widget* GetWidget() OVERRIDE; virtual const Widget* GetWidget() const OVERRIDE; @@ -110,11 +118,12 @@ class VIEWS_EXPORT DesktopNativeWidgetAura virtual bool HasCapture() const OVERRIDE; virtual InputMethod* CreateInputMethod() OVERRIDE; virtual internal::InputMethodDelegate* GetInputMethodDelegate() OVERRIDE; + virtual ui::InputMethod* GetHostInputMethod() OVERRIDE; virtual void CenterWindow(const gfx::Size& size) OVERRIDE; virtual void GetWindowPlacement( gfx::Rect* bounds, ui::WindowShowState* maximized) const OVERRIDE; - virtual bool SetWindowTitle(const string16& title) OVERRIDE; + virtual bool SetWindowTitle(const base::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; @@ -140,6 +149,7 @@ class VIEWS_EXPORT DesktopNativeWidgetAura virtual bool IsActive() const OVERRIDE; virtual void SetAlwaysOnTop(bool always_on_top) OVERRIDE; virtual bool IsAlwaysOnTop() const OVERRIDE; + virtual void SetVisibleOnAllWorkspaces(bool always_visible) OVERRIDE; virtual void Maximize() OVERRIDE; virtual void Minimize() OVERRIDE; virtual bool IsMaximized() const OVERRIDE; @@ -168,6 +178,8 @@ class VIEWS_EXPORT DesktopNativeWidgetAura virtual void SetVisibilityChangedAnimationsEnabled(bool value) OVERRIDE; virtual ui::NativeTheme* GetNativeTheme() const OVERRIDE; virtual void OnRootViewLayout() const OVERRIDE; + virtual bool IsTranslucentWindowOpacitySupported() const OVERRIDE; + virtual void RepostNativeEvent(gfx::NativeEvent native_event) OVERRIDE; // Overridden from aura::WindowDelegate: virtual gfx::Size GetMinimumSize() const OVERRIDE; @@ -183,19 +195,16 @@ class VIEWS_EXPORT DesktopNativeWidgetAura 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 OnWindowDestroying(aura::Window* window) OVERRIDE; + virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE; virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE; virtual bool HasHitTestMask() const OVERRIDE; virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE; - virtual void DidRecreateLayer(ui::Layer* old_layer, - ui::Layer* new_layer) 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: @@ -218,15 +227,15 @@ class VIEWS_EXPORT DesktopNativeWidgetAura virtual void OnDragExited() OVERRIDE; virtual int OnPerformDrop(const ui::DropTargetEvent& event) OVERRIDE; - // Overridden from aura::RootWindowObserver: - virtual void OnRootWindowHostCloseRequested( - const aura::RootWindow* root) OVERRIDE; - virtual void OnRootWindowHostResized(const aura::RootWindow* root) OVERRIDE; - virtual void OnRootWindowHostMoved(const aura::RootWindow* root, - const gfx::Point& new_origin) OVERRIDE; + // Overridden from aura::WindowTreeHostObserver: + virtual void OnHostCloseRequested(const aura::WindowTreeHost* host) OVERRIDE; + virtual void OnHostResized(const aura::WindowTreeHost* host) OVERRIDE; + virtual void OnHostMoved(const aura::WindowTreeHost* host, + const gfx::Point& new_origin) OVERRIDE; private: friend class FocusManagerEventHandler; + friend class RootWindowDestructionObserver; // Installs the input method filter. void InstallInputMethodEventFilter(); @@ -235,25 +244,20 @@ class VIEWS_EXPORT DesktopNativeWidgetAura // window is only set as transparent when the glass frame is in use. void UpdateWindowTransparency(); + void RootWindowDestroyed(); + + scoped_ptr<aura::WindowTreeHost> host_; + DesktopWindowTreeHost* desktop_window_tree_host_; + // See class documentation for Widget in widget.h for a note about ownership. Widget::InitParams::Ownership ownership_; scoped_ptr<DesktopCaptureClient> capture_client_; - // 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_; - // Can we be made active? - bool can_activate_; - - // Ownership passed to RootWindow on Init. - DesktopRootWindowHost* desktop_root_window_host_; - // Child of the root, contains |content_window_|. aura::Window* content_window_container_; @@ -264,9 +268,8 @@ class VIEWS_EXPORT DesktopNativeWidgetAura internal::NativeWidgetDelegate* native_widget_delegate_; - scoped_ptr<corewm::FocusController> focus_client_; + scoped_ptr<wm::FocusController> focus_client_; scoped_ptr<DesktopDispatcherClient> dispatcher_client_; - scoped_ptr<views::corewm::CursorManager> cursor_client_; scoped_ptr<aura::client::ScreenPositionClient> position_client_; scoped_ptr<aura::client::DragDropClient> drag_drop_client_; scoped_ptr<aura::client::WindowTreeClient> window_tree_client_; @@ -274,9 +277,9 @@ class VIEWS_EXPORT DesktopNativeWidgetAura scoped_ptr<FocusManagerEventHandler> focus_manager_event_handler_; // Toplevel event filter which dispatches to other event filters. - corewm::CompoundEventFilter* root_window_event_filter_; + scoped_ptr<wm::CompoundEventFilter> root_window_event_filter_; - scoped_ptr<corewm::InputMethodEventFilter> input_method_event_filter_; + scoped_ptr<wm::InputMethodEventFilter> input_method_event_filter_; scoped_ptr<DropHelper> drop_helper_; int last_drop_operation_; @@ -284,17 +287,25 @@ class VIEWS_EXPORT DesktopNativeWidgetAura scoped_ptr<corewm::TooltipController> tooltip_controller_; scoped_ptr<TooltipManagerAura> tooltip_manager_; - scoped_ptr<views::corewm::VisibilityController> visibility_controller_; + scoped_ptr<wm::VisibilityController> visibility_controller_; - scoped_ptr<views::corewm::WindowModalityController> + scoped_ptr<wm::WindowModalityController> window_modality_controller_; - // See comments in OnLostActive(). bool restore_focus_on_activate_; gfx::NativeCursor cursor_; - - scoped_ptr<corewm::ShadowController> shadow_controller_; + // We must manually reference count the number of users of |cursor_manager_| + // because the cursors created by |cursor_manager_| are shared among the + // DNWAs. We can't just stuff this in a LazyInstance because we need to + // destroy this as the last DNWA happens; we can't put it off until + // (potentially) after we tear down the X11 connection because that's a + // crash. + static int cursor_reference_count_; + static wm::CursorManager* cursor_manager_; + static views::DesktopNativeCursorManager* native_cursor_manager_; + + scoped_ptr<wm::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. diff --git a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc index 6a011cab955..f52fe91a7b6 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc @@ -4,13 +4,21 @@ #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" -#include "ui/aura/root_window.h" +#include "base/bind.h" +#include "ui/aura/client/aura_constants.h" +#include "ui/aura/client/cursor_client.h" +#include "ui/aura/client/window_tree_client.h" +#include "ui/aura/test/event_generator.h" #include "ui/aura/test/test_window_delegate.h" #include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" #include "ui/views/test/views_test_base.h" +#include "ui/views/test/widget_test.h" #include "ui/views/widget/widget.h" +#include "ui/wm/public/dispatcher_client.h" namespace views { +namespace test { typedef ViewsTestBase DesktopNativeWidgetAuraTest; @@ -27,12 +35,21 @@ TEST_F(DesktopNativeWidgetAuraTest, CreateWithParentNotInRootWindow) { widget.Init(params); } -// Verifies that the AURA windows making up a widget instance have the correct +// Verifies that the Aura windows making up a widget instance have the correct // bounds after the widget is resized. TEST_F(DesktopNativeWidgetAuraTest, DesktopAuraWindowSizeTest) { Widget widget; + + // On Linux we test this with popup windows because the WM may ignore the size + // suggestion for normal windows. +#if defined(OS_LINUX) + Widget::InitParams init_params = + CreateParams(Widget::InitParams::TYPE_POPUP); +#else Widget::InitParams init_params = CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); +#endif + init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; init_params.native_widget = new DesktopNativeWidgetAura(&widget); widget.Init(init_params); @@ -68,6 +85,97 @@ TEST_F(DesktopNativeWidgetAuraTest, NativeViewInitiallyHidden) { EXPECT_FALSE(widget.GetNativeView()->IsVisible()); } +// Verify that the cursor state is shared between two native widgets. +TEST_F(DesktopNativeWidgetAuraTest, GlobalCursorState) { + // Create two native widgets, each owning different root windows. + Widget widget_a; + Widget::InitParams init_params_a = + CreateParams(Widget::InitParams::TYPE_WINDOW); + init_params_a.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + DesktopNativeWidgetAura* desktop_native_widget_aura_a = + new DesktopNativeWidgetAura(&widget_a); + init_params_a.native_widget = desktop_native_widget_aura_a; + widget_a.Init(init_params_a); + + Widget widget_b; + Widget::InitParams init_params_b = + CreateParams(Widget::InitParams::TYPE_WINDOW); + init_params_b.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + DesktopNativeWidgetAura* desktop_native_widget_aura_b = + new DesktopNativeWidgetAura(&widget_b); + init_params_b.native_widget = desktop_native_widget_aura_b; + widget_b.Init(init_params_b); + + aura::client::CursorClient* cursor_client_a = aura::client::GetCursorClient( + desktop_native_widget_aura_a->host()->window()); + aura::client::CursorClient* cursor_client_b = aura::client::GetCursorClient( + desktop_native_widget_aura_b->host()->window()); + + // Verify the cursor can be locked using one client and unlocked using + // another. + EXPECT_FALSE(cursor_client_a->IsCursorLocked()); + EXPECT_FALSE(cursor_client_b->IsCursorLocked()); + + cursor_client_a->LockCursor(); + EXPECT_TRUE(cursor_client_a->IsCursorLocked()); + EXPECT_TRUE(cursor_client_b->IsCursorLocked()); + + cursor_client_b->UnlockCursor(); + EXPECT_FALSE(cursor_client_a->IsCursorLocked()); + EXPECT_FALSE(cursor_client_b->IsCursorLocked()); + + // Verify that mouse events can be disabled using one client and then + // re-enabled using another. Note that disabling mouse events should also + // have the side effect of making the cursor invisible. + EXPECT_TRUE(cursor_client_a->IsCursorVisible()); + EXPECT_TRUE(cursor_client_b->IsCursorVisible()); + EXPECT_TRUE(cursor_client_a->IsMouseEventsEnabled()); + EXPECT_TRUE(cursor_client_b->IsMouseEventsEnabled()); + + cursor_client_b->DisableMouseEvents(); + EXPECT_FALSE(cursor_client_a->IsCursorVisible()); + EXPECT_FALSE(cursor_client_b->IsCursorVisible()); + EXPECT_FALSE(cursor_client_a->IsMouseEventsEnabled()); + EXPECT_FALSE(cursor_client_b->IsMouseEventsEnabled()); + + cursor_client_a->EnableMouseEvents(); + EXPECT_TRUE(cursor_client_a->IsCursorVisible()); + EXPECT_TRUE(cursor_client_b->IsCursorVisible()); + EXPECT_TRUE(cursor_client_a->IsMouseEventsEnabled()); + EXPECT_TRUE(cursor_client_b->IsMouseEventsEnabled()); + + // Verify that setting the cursor using one cursor client + // will set it for all root windows. + EXPECT_EQ(ui::kCursorNone, cursor_client_a->GetCursor().native_type()); + EXPECT_EQ(ui::kCursorNone, cursor_client_b->GetCursor().native_type()); + + cursor_client_b->SetCursor(ui::kCursorPointer); + EXPECT_EQ(ui::kCursorPointer, cursor_client_a->GetCursor().native_type()); + EXPECT_EQ(ui::kCursorPointer, cursor_client_b->GetCursor().native_type()); + + // Verify that hiding the cursor using one cursor client will + // hide it for all root windows. Note that hiding the cursor + // should not disable mouse events. + cursor_client_a->HideCursor(); + EXPECT_FALSE(cursor_client_a->IsCursorVisible()); + EXPECT_FALSE(cursor_client_b->IsCursorVisible()); + EXPECT_TRUE(cursor_client_a->IsMouseEventsEnabled()); + EXPECT_TRUE(cursor_client_b->IsMouseEventsEnabled()); + + // Verify that the visibility state cannot be changed using one + // cursor client when the cursor was locked using another. + cursor_client_b->LockCursor(); + cursor_client_a->ShowCursor(); + EXPECT_FALSE(cursor_client_a->IsCursorVisible()); + EXPECT_FALSE(cursor_client_b->IsCursorVisible()); + + // Verify the cursor becomes visible on unlock (since a request + // to make it visible was queued up while the cursor was locked). + cursor_client_b->UnlockCursor(); + EXPECT_TRUE(cursor_client_a->IsCursorVisible()); + EXPECT_TRUE(cursor_client_b->IsCursorVisible()); +} + // Verifies FocusController doesn't attempt to access |content_window_| during // destruction. Previously the FocusController was destroyed after the window. // This could be problematic as FocusController references |content_window_| and @@ -94,4 +202,198 @@ TEST_F(DesktopNativeWidgetAuraTest, DontAccessContentWindowDuringDestruction) { } } +void QuitNestedLoopAndCloseWidget(scoped_ptr<Widget> widget, + base::Closure* quit_runloop) { + quit_runloop->Run(); +} + +// Verifies that a widget can be destroyed when running a nested message-loop. +TEST_F(DesktopNativeWidgetAuraTest, WidgetCanBeDestroyedFromNestedLoop) { + scoped_ptr<Widget> widget(new Widget); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); + params.bounds = gfx::Rect(0, 0, 200, 200); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.native_widget = new DesktopNativeWidgetAura(widget.get()); + widget->Init(params); + widget->Show(); + + aura::Window* window = widget->GetNativeView(); + aura::Window* root = window->GetRootWindow(); + aura::client::DispatcherClient* client = + aura::client::GetDispatcherClient(root); + + // Post a task that terminates the nested loop and destroyes the widget. This + // task will be executed from the nested loop initiated with the call to + // |RunWithDispatcher()| below. + aura::client::DispatcherRunLoop run_loop(client, NULL); + base::Closure quit_runloop = run_loop.QuitClosure(); + message_loop()->PostTask(FROM_HERE, + base::Bind(&QuitNestedLoopAndCloseWidget, + base::Passed(&widget), + base::Unretained(&quit_runloop))); + run_loop.Run(); +} + +// This class provides functionality to create fullscreen and top level popup +// windows. It additionally tests whether the destruction of these 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 DesktopAuraTopLevelWindowTest + : public views::TestViewsDelegate, + public aura::WindowObserver { + public: + DesktopAuraTopLevelWindowTest() + : top_level_widget_(NULL), + owned_window_(NULL), + owner_destroyed_(false), + owned_window_destroyed_(false) {} + + virtual ~DesktopAuraTopLevelWindowTest() { + EXPECT_TRUE(owner_destroyed_); + EXPECT_TRUE(owned_window_destroyed_); + top_level_widget_ = NULL; + owned_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 CreateTopLevelWindow(const gfx::Rect& bounds, bool fullscreen) { + 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 = aura::WINDOW_LAYER_NOT_DRAWN; + init_params.accept_events = fullscreen; + + widget_.Init(init_params); + + owned_window_ = new aura::Window(&child_window_delegate_); + owned_window_->SetType(ui::wm::WINDOW_TYPE_NORMAL); + owned_window_->SetName("TestTopLevelWindow"); + if (fullscreen) { + owned_window_->SetProperty(aura::client::kShowStateKey, + ui::SHOW_STATE_FULLSCREEN); + } else { + owned_window_->SetType(ui::wm::WINDOW_TYPE_MENU); + } + owned_window_->Init(aura::WINDOW_LAYER_TEXTURED); + aura::client::ParentWindowWithContext( + owned_window_, + widget_.GetNativeView()->GetRootWindow(), + gfx::Rect(0, 0, 1900, 1600)); + owned_window_->Show(); + owned_window_->AddObserver(this); + + ASSERT_TRUE(owned_window_->parent() != NULL); + owned_window_->parent()->AddObserver(this); + + top_level_widget_ = + views::Widget::GetWidgetForNativeView(owned_window_->parent()); + ASSERT_TRUE(top_level_widget_ != NULL); + } + + void DestroyOwnedWindow() { + ASSERT_TRUE(owned_window_ != NULL); + delete owned_window_; + } + + void DestroyOwnerWindow() { + ASSERT_TRUE(top_level_widget_ != NULL); + top_level_widget_->CloseNow(); + } + + virtual void OnWindowDestroying(aura::Window* window) OVERRIDE { + window->RemoveObserver(this); + if (window == owned_window_) { + owned_window_destroyed_ = true; + } else if (window == top_level_widget_->GetNativeView()) { + owner_destroyed_ = true; + } else { + ADD_FAILURE() << "Unexpected window destroyed callback: " << window; + } + } + + aura::Window* owned_window() { + return owned_window_; + } + + views::Widget* top_level_widget() { + return top_level_widget_; + } + + private: + views::Widget widget_; + views::Widget* top_level_widget_; + aura::Window* owned_window_; + bool owner_destroyed_; + bool owned_window_destroyed_; + aura::test::TestWindowDelegate child_window_delegate_; + + DISALLOW_COPY_AND_ASSIGN(DesktopAuraTopLevelWindowTest); +}; + +typedef WidgetTest DesktopAuraWidgetTest; + +TEST_F(DesktopAuraWidgetTest, FullscreenWindowDestroyedBeforeOwnerTest) { + ViewsDelegate::views_delegate = NULL; + DesktopAuraTopLevelWindowTest fullscreen_window; + ASSERT_NO_FATAL_FAILURE(fullscreen_window.CreateTopLevelWindow( + gfx::Rect(0, 0, 200, 200), true)); + + RunPendingMessages(); + ASSERT_NO_FATAL_FAILURE(fullscreen_window.DestroyOwnedWindow()); + RunPendingMessages(); +} + +TEST_F(DesktopAuraWidgetTest, FullscreenWindowOwnerDestroyed) { + ViewsDelegate::views_delegate = NULL; + + DesktopAuraTopLevelWindowTest fullscreen_window; + ASSERT_NO_FATAL_FAILURE(fullscreen_window.CreateTopLevelWindow( + gfx::Rect(0, 0, 200, 200), true)); + + RunPendingMessages(); + ASSERT_NO_FATAL_FAILURE(fullscreen_window.DestroyOwnerWindow()); + RunPendingMessages(); +} + +TEST_F(DesktopAuraWidgetTest, TopLevelOwnedPopupTest) { + ViewsDelegate::views_delegate = NULL; + DesktopAuraTopLevelWindowTest popup_window; + ASSERT_NO_FATAL_FAILURE(popup_window.CreateTopLevelWindow( + gfx::Rect(0, 0, 200, 200), false)); + + RunPendingMessages(); + ASSERT_NO_FATAL_FAILURE(popup_window.DestroyOwnedWindow()); + RunPendingMessages(); +} + +// This test validates that when a top level owned popup Aura window is +// resized, the widget is resized as well. +TEST_F(DesktopAuraWidgetTest, TopLevelOwnedPopupResizeTest) { + ViewsDelegate::views_delegate = NULL; + DesktopAuraTopLevelWindowTest popup_window; + ASSERT_NO_FATAL_FAILURE(popup_window.CreateTopLevelWindow( + gfx::Rect(0, 0, 200, 200), false)); + + gfx::Rect new_size(0, 0, 400, 400); + popup_window.owned_window()->SetBounds(new_size); + + EXPECT_EQ(popup_window.top_level_widget()->GetNativeView()->bounds().size(), + new_size.size()); + RunPendingMessages(); + ASSERT_NO_FATAL_FAILURE(popup_window.DestroyOwnedWindow()); + RunPendingMessages(); +} + +} // namespace test } // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_win_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_win_unittest.cc deleted file mode 100644 index a002f10fdf2..00000000000 --- a/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_win_unittest.cc +++ /dev/null @@ -1,55 +0,0 @@ -// 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/desktop_aura/desktop_root_window_host_win.h" - -#include "ui/aura/root_window.h" -#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" - -namespace views { - -typedef ViewsTestBase DesktopRootWindowHostWinTest; - -namespace { - -// See description above SaveFocusOnDeactivateFromHandleCreate. -class TestDesktopRootWindowHostWin : public DesktopRootWindowHostWin { - public: - TestDesktopRootWindowHostWin( - internal::NativeWidgetDelegate* native_widget_delegate, - DesktopNativeWidgetAura* desktop_native_widget_aura) - : DesktopRootWindowHostWin(native_widget_delegate, - desktop_native_widget_aura) {} - virtual ~TestDesktopRootWindowHostWin() {} - - // DesktopRootWindowHostWin overrides: - virtual void HandleCreate() OVERRIDE { - DesktopRootWindowHostWin::HandleCreate(); - SaveFocusOnDeactivate(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(TestDesktopRootWindowHostWin); -}; - -} // namespace - -// Verifies if SaveFocusOnDeactivate() is invoked from -// DesktopRootWindowHostWin::HandleCreate we don't crash. -TEST_F(DesktopRootWindowHostWinTest, SaveFocusOnDeactivateFromHandleCreate) { - Widget widget; - Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); - params.bounds = gfx::Rect(0, 0, 200, 200); - params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - DesktopNativeWidgetAura* desktop_native_widget_aura = - new DesktopNativeWidgetAura(&widget); - params.native_widget = desktop_native_widget_aura; - params.desktop_root_window_host = new TestDesktopRootWindowHostWin( - &widget, desktop_native_widget_aura); - widget.Init(params); -} - -} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen.h b/chromium/ui/views/widget/desktop_aura/desktop_screen.h index ad66bd41575..2db961d2d95 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen.h @@ -14,7 +14,7 @@ class Screen; namespace views { // Creates a Screen that represents the screen of the environment that hosts -// a RootWindowHost. Caller owns the result. +// a WindowTreeHost. Caller owns the result. VIEWS_EXPORT gfx::Screen* CreateDesktopScreen(); } // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc index 4d7f9a1b739..9708ad88bfc 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc @@ -4,14 +4,12 @@ #include "ui/views/widget/desktop_aura/desktop_screen.h" -#include "ui/gfx/ozone/surface_factory_ozone.h" -#include "ui/ozone/ozone_platform.h" +#include "ui/views/widget/desktop_aura/desktop_factory_ozone.h" namespace views { gfx::Screen* CreateDesktopScreen() { - ui::OzonePlatform::Initialize(); - return gfx::SurfaceFactoryOzone::GetInstance()->CreateDesktopScreen(); + return DesktopFactoryOzone::GetInstance()->CreateDesktopScreen(); } } // namespace views 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 index a16b24b6427..96deaf4205c 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_position_client.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_position_client.cc @@ -4,7 +4,7 @@ #include "ui/views/widget/desktop_aura/desktop_screen_position_client.h" -#include "ui/aura/root_window.h" +#include "ui/aura/window_tree_host.h" #include "ui/gfx/display.h" #include "ui/gfx/point_conversions.h" #include "ui/gfx/screen.h" @@ -15,20 +15,17 @@ namespace views { namespace { gfx::Point GetOrigin(const aura::Window* root_window) { - gfx::Point origin_in_pixels = - root_window->GetDispatcher()->host()->GetBounds().origin(); + gfx::Point origin_in_pixels = root_window->GetHost()->GetBounds().origin(); aura::Window* window = const_cast<aura::Window*>(root_window); float scale = gfx::Screen::GetScreenFor(window)-> GetDisplayNearestWindow(window).device_scale_factor(); - return gfx::ToFlooredPoint( - gfx::ScalePoint(origin_in_pixels, 1 / scale)); + 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) + if (window->type() == ui::wm::WINDOW_TYPE_POPUP) return true; Widget* widget = Widget::GetWidgetForNativeView(window); @@ -37,14 +34,19 @@ bool PositionWindowInScreenCoordinates(aura::Window* window) { } // namespace -DesktopScreenPositionClient::DesktopScreenPositionClient() { +DesktopScreenPositionClient::DesktopScreenPositionClient( + aura::Window* root_window) + : root_window_(root_window) { + aura::client::SetScreenPositionClient(root_window_, this); } DesktopScreenPositionClient::~DesktopScreenPositionClient() { + aura::client::SetScreenPositionClient(root_window_, NULL); } void DesktopScreenPositionClient::ConvertPointToScreen( - const aura::Window* window, gfx::Point* point) { + const aura::Window* window, + gfx::Point* point) { const aura::Window* root_window = window->GetRootWindow(); aura::Window::ConvertPointToTarget(window, root_window, point); gfx::Point origin = GetOrigin(root_window); @@ -52,26 +54,33 @@ void DesktopScreenPositionClient::ConvertPointToScreen( } void DesktopScreenPositionClient::ConvertPointFromScreen( - const aura::Window* window, gfx::Point* point) { + const aura::Window* window, + gfx::Point* point) { const aura::Window* 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::Window* window, gfx::Point* point) { +void DesktopScreenPositionClient::ConvertHostPointToScreen(aura::Window* window, + gfx::Point* point) { aura::Window* root_window = window->GetRootWindow(); ConvertPointToScreen(root_window, point); } -void DesktopScreenPositionClient::SetBounds( - aura::Window* window, - const gfx::Rect& bounds, - const gfx::Display& display) { +void DesktopScreenPositionClient::SetBounds(aura::Window* window, + const gfx::Rect& bounds, + const gfx::Display& display) { // TODO: Use the 3rd parameter, |display|. aura::Window* root = window->GetRootWindow(); + // This method assumes that |window| does not have an associated + // DesktopNativeWidgetAura. + internal::NativeWidgetPrivate* desktop_native_widget = + DesktopNativeWidgetAura::ForWindow(root); + DCHECK(!desktop_native_widget || + desktop_native_widget->GetNativeView() != window); + 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 @@ -86,14 +95,7 @@ void DesktopScreenPositionClient::SetBounds( return; } - DesktopNativeWidgetAura* desktop_native_widget = - DesktopNativeWidgetAura::ForWindow(window); - if (desktop_native_widget) { - root->GetDispatcher()->SetHostBounds(bounds); - // Setting bounds of root resizes |window|. - } else { - window->SetBounds(bounds); - } + 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 index da8ad402ea7..222c2a1a4ea 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_position_client.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_position_client.h @@ -15,7 +15,7 @@ namespace views { class VIEWS_EXPORT DesktopScreenPositionClient : public aura::client::ScreenPositionClient { public: - DesktopScreenPositionClient(); + explicit DesktopScreenPositionClient(aura::Window* root_window); virtual ~DesktopScreenPositionClient(); // aura::client::ScreenPositionClient overrides: @@ -28,6 +28,11 @@ class VIEWS_EXPORT DesktopScreenPositionClient virtual void SetBounds(aura::Window* window, const gfx::Rect& bounds, const gfx::Display& display) OVERRIDE; + + private: + aura::Window* root_window_; + + DISALLOW_COPY_AND_ASSIGN(DesktopScreenPositionClient); }; } // namespace views 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 index bb1cd8b08ac..ac30d1ee700 100644 --- 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 @@ -15,21 +15,17 @@ typedef ViewsTestBase DesktopScreenPositionClientTest; // 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(); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); + params.bounds = gfx::Rect(10, 11, 200, 200); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.native_widget = new DesktopNativeWidgetAura(&parent_widget); + parent_widget.Init(params); // 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_delegate_view, NULL, parent_widget.GetNativeView()); dialog->SetBounds(gfx::Rect(11, 12, 200, 200)); EXPECT_EQ("11,12", dialog->GetWindowBoundsInScreen().origin().ToString()); } diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_win.cc index 86823a04d3a..c5edf76c687 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_win.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_win.cc @@ -5,12 +5,12 @@ #include "ui/views/widget/desktop_aura/desktop_screen_win.h" #include "base/logging.h" -#include "ui/aura/root_window.h" #include "ui/aura/window.h" +#include "ui/aura/window_event_dispatcher.h" #include "ui/aura/window_tree_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" +#include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h" namespace { @@ -54,13 +54,13 @@ gfx::Display DesktopScreenWin::GetDisplayMatching( } HWND DesktopScreenWin::GetHWNDFromNativeView(gfx::NativeView window) const { - aura::WindowEventDispatcher* dispatcher = window->GetDispatcher(); - return dispatcher ? dispatcher->host()->GetAcceleratedWidget() : NULL; + aura::WindowTreeHost* host = window->GetHost(); + return host ? host->GetAcceleratedWidget() : NULL; } gfx::NativeWindow DesktopScreenWin::GetNativeWindowFromHWND(HWND hwnd) const { return (::IsWindow(hwnd)) ? - DesktopRootWindowHostWin::GetContentWindowForHWND(hwnd) : NULL; + DesktopWindowTreeHostWin::GetContentWindowForHWND(hwnd) : NULL; } //////////////////////////////////////////////////////////////////////////////// diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc index 332b81c59ce..76db7948117 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc @@ -12,19 +12,21 @@ #include "base/debug/trace_event.h" #include "base/logging.h" -#include "base/x11/edid_parser_x11.h" -#include "ui/aura/root_window.h" #include "ui/aura/window.h" +#include "ui/aura/window_event_dispatcher.h" #include "ui/aura/window_tree_host.h" #include "ui/base/layout.h" -#include "ui/base/x/x11_util.h" +#include "ui/display/util/display_util.h" +#include "ui/display/util/x11/edid_parser_x11.h" +#include "ui/events/platform/platform_event_source.h" #include "ui/gfx/display.h" #include "ui/gfx/display_observer.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/screen.h" #include "ui/gfx/x/x11_types.h" -#include "ui/views/widget/desktop_aura/desktop_root_window_host_x11.h" #include "ui/views/widget/desktop_aura/desktop_screen.h" +#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" +#include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h" namespace { @@ -32,6 +34,7 @@ namespace { // in |Dispatch()|. const int64 kConfigureDelayMs = 500; +// TODO(oshima): Consider using gtk-xft-dpi instead. float GetDeviceScaleFactor(int screen_pixels, int screen_mm) { const int kCSSDefaultDPI = 96; const float kInchInMm = 25.4f; @@ -40,7 +43,7 @@ float GetDeviceScaleFactor(int screen_pixels, int screen_mm) { float screen_dpi = screen_pixels / screen_inches; float scale = screen_dpi / kCSSDefaultDPI; - return ui::GetImageScale(ui::GetSupportedScaleFactor(scale)); + return ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactor(scale)); } std::vector<gfx::Display> GetFallbackDisplayList() { @@ -48,14 +51,14 @@ std::vector<gfx::Display> GetFallbackDisplayList() { ::Screen* screen = DefaultScreenOfDisplay(display); int width = WidthOfScreen(screen); int height = HeightOfScreen(screen); - int mm_width = WidthMMOfScreen(screen); - int mm_height = HeightMMOfScreen(screen); + gfx::Size physical_size(WidthMMOfScreen(screen), HeightMMOfScreen(screen)); gfx::Rect bounds_in_pixels(0, 0, width, height); gfx::Display gfx_display(0, bounds_in_pixels); if (!gfx::Display::HasForceDeviceScaleFactor() && - !ui::IsXDisplaySizeBlackListed(mm_width, mm_height)) { - float device_scale_factor = GetDeviceScaleFactor(width, mm_width); + !ui::IsDisplaySizeBlackListed(physical_size)) { + float device_scale_factor = GetDeviceScaleFactor( + width, physical_size.width()); DCHECK_LE(1.0f, device_scale_factor); gfx_display.SetScaleAndBounds(device_scale_factor, bounds_in_pixels); } @@ -71,7 +74,7 @@ namespace views { // DesktopScreenX11, public: DesktopScreenX11::DesktopScreenX11() - : xdisplay_(base::MessagePumpX11::GetDefaultXDisplay()), + : xdisplay_(gfx::GetXDisplay()), x_root_window_(DefaultRootWindow(xdisplay_)), has_xrandr_(false), xrandr_event_base_(0) { @@ -88,10 +91,13 @@ DesktopScreenX11::DesktopScreenX11() int error_base_ignored = 0; XRRQueryExtension(xdisplay_, &xrandr_event_base_, &error_base_ignored); - base::MessagePumpX11::Current()->AddDispatcherForRootWindow(this); + if (ui::PlatformEventSource::GetInstance()) + ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); XRRSelectInput(xdisplay_, x_root_window_, - RRScreenChangeNotifyMask | RROutputChangeNotifyMask); + RRScreenChangeNotifyMask | + RROutputChangeNotifyMask | + RRCrtcChangeNotifyMask); displays_ = BuildDisplaysFromXRandRInfo(); } else { @@ -100,18 +106,22 @@ DesktopScreenX11::DesktopScreenX11() } DesktopScreenX11::~DesktopScreenX11() { - if (has_xrandr_) - base::MessagePumpX11::Current()->RemoveDispatcherForRootWindow(this); + if (has_xrandr_ && ui::PlatformEventSource::GetInstance()) + ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); } void DesktopScreenX11::ProcessDisplayChange( const std::vector<gfx::Display>& incoming) { - std::vector<gfx::Display>::const_iterator cur_it = displays_.begin(); - for (; cur_it != displays_.end(); ++cur_it) { + std::vector<gfx::Display> old_displays = displays_; + displays_ = incoming; + + typedef std::vector<gfx::Display>::const_iterator DisplayIt; + std::vector<gfx::Display>::const_iterator old_it = old_displays.begin(); + for (; old_it != old_displays.end(); ++old_it) { bool found = false; - for (std::vector<gfx::Display>::const_iterator incoming_it = - incoming.begin(); incoming_it != incoming.end(); ++incoming_it) { - if (cur_it->id() == incoming_it->id()) { + for (std::vector<gfx::Display>::const_iterator new_it = + displays_.begin(); new_it != displays_.end(); ++new_it) { + if (old_it->id() == new_it->id()) { found = true; break; } @@ -119,33 +129,47 @@ void DesktopScreenX11::ProcessDisplayChange( if (!found) { FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_, - OnDisplayRemoved(*cur_it)); + OnDisplayRemoved(*old_it)); } } - std::vector<gfx::Display>::const_iterator incoming_it = incoming.begin(); - for (; incoming_it != incoming.end(); ++incoming_it) { + std::vector<gfx::Display>::const_iterator new_it = displays_.begin(); + for (; new_it != displays_.end(); ++new_it) { bool found = false; - for (std::vector<gfx::Display>::const_iterator cur_it = displays_.begin(); - cur_it != displays_.end(); ++cur_it) { - if (incoming_it->id() == cur_it->id()) { - if (incoming_it->bounds() != cur_it->bounds()) { - FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_, - OnDisplayBoundsChanged(*incoming_it)); - } + for (std::vector<gfx::Display>::const_iterator old_it = + old_displays.begin(); old_it != old_displays.end(); ++old_it) { + if (new_it->id() != old_it->id()) + continue; - found = true; - break; + uint32_t metrics = gfx::DisplayObserver::DISPLAY_METRIC_NONE; + + if (new_it->bounds() != old_it->bounds()) + metrics |= gfx::DisplayObserver::DISPLAY_METRIC_BOUNDS; + + if (new_it->rotation() != old_it->rotation()) + metrics |= gfx::DisplayObserver::DISPLAY_METRIC_ROTATION; + + if (new_it->work_area() != old_it->work_area()) + metrics |= gfx::DisplayObserver::DISPLAY_METRIC_WORK_AREA; + + if (new_it->device_scale_factor() != old_it->device_scale_factor()) + metrics |= gfx::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR; + + if (metrics != gfx::DisplayObserver::DISPLAY_METRIC_NONE) { + FOR_EACH_OBSERVER(gfx::DisplayObserver, + observer_list_, + OnDisplayMetricsChanged(*new_it, metrics)); } + + found = true; + break; } if (!found) { FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_, - OnDisplayAdded(*incoming_it)); + OnDisplayAdded(*new_it)); } } - - displays_ = incoming; } //////////////////////////////////////////////////////////////////////////////// @@ -182,16 +206,8 @@ gfx::NativeWindow DesktopScreenX11::GetWindowUnderCursor() { gfx::NativeWindow DesktopScreenX11::GetWindowAtScreenPoint( const gfx::Point& point) { - std::vector<aura::Window*> windows = - DesktopRootWindowHostX11::GetAllOpenWindows(); - - for (std::vector<aura::Window*>::const_iterator it = windows.begin(); - it != windows.end(); ++it) { - if ((*it)->GetBoundsInScreen().Contains(point)) - return *it; - } - - return NULL; + X11TopmostWindowFinder finder; + return finder.FindLocalProcessWindowAt(point, std::set<aura::Window*>()); } int DesktopScreenX11::GetNumDisplays() const { @@ -207,16 +223,16 @@ gfx::Display DesktopScreenX11::GetDisplayNearestWindow( // Getting screen bounds here safely is hard. // // You'd think we'd be able to just call window->GetBoundsInScreen(), but we - // can't because |window| (and the associated RootWindow*) can be partially - // initialized at this point; RootWindow initializations call through into - // GetDisplayNearestWindow(). But the X11 resources are created before we - // create the aura::RootWindow. So we ask what the DRWHX11 believes the - // window bounds are instead of going through the aura::Window's screen - // bounds. - aura::WindowEventDispatcher* dispatcher = window->GetDispatcher(); - if (dispatcher) { - DesktopRootWindowHostX11* rwh = DesktopRootWindowHostX11::GetHostForXID( - dispatcher->host()->GetAcceleratedWidget()); + // can't because |window| (and the associated WindowEventDispatcher*) can be + // partially initialized at this point; WindowEventDispatcher initializations + // call through into GetDisplayNearestWindow(). But the X11 resources are + // created before we create the aura::WindowEventDispatcher. So we ask what + // the DRWHX11 believes the window bounds are instead of going through the + // aura::Window's screen bounds. + aura::WindowTreeHost* host = window->GetHost(); + if (host) { + DesktopWindowTreeHostX11* rwh = DesktopWindowTreeHostX11::GetHostForXID( + host->GetAcceleratedWidget()); if (rwh) return GetDisplayMatching(rwh->GetX11RootWindowBounds()); } @@ -264,7 +280,12 @@ void DesktopScreenX11::RemoveObserver(gfx::DisplayObserver* observer) { observer_list_.RemoveObserver(observer); } -bool DesktopScreenX11::Dispatch(const base::NativeEvent& event) { +bool DesktopScreenX11::CanDispatchEvent(const ui::PlatformEvent& event) { + return event->type - xrandr_event_base_ == RRScreenChangeNotify || + event->type - xrandr_event_base_ == RRNotify; +} + +uint32_t DesktopScreenX11::DispatchEvent(const ui::PlatformEvent& event) { if (event->type - xrandr_event_base_ == RRScreenChangeNotify) { // Pass the event through to xlib. XRRUpdateConfiguration(event); @@ -281,9 +302,11 @@ bool DesktopScreenX11::Dispatch(const base::NativeEvent& event) { this, &DesktopScreenX11::ConfigureTimerFired); } + } else { + NOTREACHED(); } - return true; + return ui::POST_DISPATCH_NONE; } //////////////////////////////////////////////////////////////////////////////// @@ -291,7 +314,7 @@ bool DesktopScreenX11::Dispatch(const base::NativeEvent& event) { DesktopScreenX11::DesktopScreenX11( const std::vector<gfx::Display>& test_displays) - : xdisplay_(base::MessagePumpX11::GetDefaultXDisplay()), + : xdisplay_(gfx::GetXDisplay()), x_root_window_(DefaultRootWindow(xdisplay_)), has_xrandr_(false), xrandr_event_base_(0), @@ -334,7 +357,7 @@ std::vector<gfx::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() { output_info->crtc); int64 display_id = -1; - if (!base::GetDisplayId(output_id, i, &display_id)) { + if (!ui::GetDisplayId(output_id, static_cast<uint8>(i), &display_id)) { // It isn't ideal, but if we can't parse the EDID data, fallback on the // display number. display_id = i; @@ -344,8 +367,8 @@ std::vector<gfx::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() { gfx::Display display(display_id, crtc_bounds); if (!gfx::Display::HasForceDeviceScaleFactor()) { - if (i == 0 && !ui::IsXDisplaySizeBlackListed(output_info->mm_width, - output_info->mm_height)) { + if (i == 0 && !ui::IsDisplaySizeBlackListed( + gfx::Size(output_info->mm_width, output_info->mm_height))) { // As per display scale factor is not supported right now, // the primary display's scale factor is always used. device_scale_factor = GetDeviceScaleFactor(crtc->width, @@ -361,6 +384,21 @@ std::vector<gfx::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() { display.set_work_area(intersection); } + switch (crtc->rotation) { + case RR_Rotate_0: + display.set_rotation(gfx::Display::ROTATE_0); + break; + case RR_Rotate_90: + display.set_rotation(gfx::Display::ROTATE_90); + break; + case RR_Rotate_180: + display.set_rotation(gfx::Display::ROTATE_180); + break; + case RR_Rotate_270: + display.set_rotation(gfx::Display::ROTATE_270); + break; + } + displays.push_back(display); XRRFreeCrtcInfo(crtc); diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h index 1b9a8320a36..a5b2a17dd83 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h @@ -5,9 +5,9 @@ #ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_X11_H_ #define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_X11_H_ -#include "base/message_loop/message_pump_dispatcher.h" #include "base/observer_list.h" #include "base/timer/timer.h" +#include "ui/events/platform/platform_event_dispatcher.h" #include "ui/gfx/screen.h" #include "ui/views/views_export.h" @@ -24,7 +24,7 @@ class DesktopScreenX11Test; // Our singleton screen implementation that talks to xrandr. class VIEWS_EXPORT DesktopScreenX11 : public gfx::Screen, - public base::MessagePumpDispatcher { + public ui::PlatformEventDispatcher { public: DesktopScreenX11(); @@ -52,8 +52,9 @@ class VIEWS_EXPORT DesktopScreenX11 : public gfx::Screen, virtual void AddObserver(gfx::DisplayObserver* observer) OVERRIDE; virtual void RemoveObserver(gfx::DisplayObserver* observer) OVERRIDE; - // Overridden from MessagePumpDispatcher: - virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE; + // ui::PlatformEventDispatcher: + virtual bool CanDispatchEvent(const ui::PlatformEvent& event) OVERRIDE; + virtual uint32_t DispatchEvent(const ui::PlatformEvent& event) OVERRIDE; private: friend class DesktopScreenX11Test; diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc index 55ce07069fb..c9e7159a53f 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc @@ -4,12 +4,47 @@ #include "ui/views/widget/desktop_aura/desktop_screen_x11.h" -#include "base/message_loop/message_loop.h" +#include "base/memory/scoped_ptr.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/aura/client/aura_constants.h" +#include "ui/aura/test/event_generator.h" +#include "ui/aura/window.h" +#include "ui/aura/window_event_dispatcher.h" +#include "ui/base/hit_test.h" +#include "ui/base/x/x11_util.h" #include "ui/gfx/display_observer.h" +#include "ui/gfx/x/x11_types.h" #include "ui/views/test/views_test_base.h" #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" -#include "ui/views/widget/desktop_aura/desktop_root_window_host_x11.h" +#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" + +namespace { + +// Class which allows for the designation of non-client component targets of +// hit tests. +class TestDesktopNativeWidgetAura : public views::DesktopNativeWidgetAura { + public: + explicit TestDesktopNativeWidgetAura( + views::internal::NativeWidgetDelegate* delegate) + : views::DesktopNativeWidgetAura(delegate) {} + virtual ~TestDesktopNativeWidgetAura() {} + + void set_window_component(int window_component) { + window_component_ = window_component; + } + + // DesktopNativeWidgetAura: + virtual int GetNonClientComponent(const gfx::Point& point) const OVERRIDE { + return window_component_; + } + + private: + int window_component_; + + DISALLOW_COPY_AND_ASSIGN(TestDesktopNativeWidgetAura); +}; + +} // namespace namespace views { @@ -50,23 +85,26 @@ class DesktopScreenX11Test : public views::ViewsTestBase, removed_display_.clear(); } - Widget* BuildTopLevelDesktopWidget(const gfx::Rect& bounds) { + Widget* BuildTopLevelDesktopWidget(const gfx::Rect& bounds, + bool use_test_native_widget) { Widget* toplevel = new Widget; Widget::InitParams toplevel_params = CreateParams(Widget::InitParams::TYPE_WINDOW); - toplevel_params.native_widget = - new views::DesktopNativeWidgetAura(toplevel); + if (use_test_native_widget) { + toplevel_params.native_widget = + new TestDesktopNativeWidgetAura(toplevel); + } else { + toplevel_params.native_widget = + new views::DesktopNativeWidgetAura(toplevel); + } toplevel_params.bounds = bounds; + toplevel_params.remove_standard_frame = true; toplevel->Init(toplevel_params); return toplevel; } private: // Overridden from gfx::DisplayObserver: - virtual void OnDisplayBoundsChanged(const gfx::Display& display) OVERRIDE { - changed_display_.push_back(display); - } - virtual void OnDisplayAdded(const gfx::Display& new_display) OVERRIDE { added_display_.push_back(new_display); } @@ -75,6 +113,11 @@ class DesktopScreenX11Test : public views::ViewsTestBase, removed_display_.push_back(old_display); } + virtual void OnDisplayMetricsChanged(const gfx::Display& display, + uint32_t metrics) OVERRIDE { + changed_display_.push_back(display); + } + scoped_ptr<DesktopScreenX11> screen_; DISALLOW_COPY_AND_ASSIGN(DesktopScreenX11Test); @@ -198,32 +241,6 @@ TEST_F(DesktopScreenX11Test, GetPrimaryDisplay) { EXPECT_EQ(kFirstDisplay, screen()->GetPrimaryDisplay().id()); } -TEST_F(DesktopScreenX11Test, GetWindowAtScreenPoint) { - Widget* window_one = BuildTopLevelDesktopWidget(gfx::Rect(10, 10, 10, 10)); - Widget* window_two = BuildTopLevelDesktopWidget(gfx::Rect(50, 50, 10, 10)); - Widget* window_three = BuildTopLevelDesktopWidget(gfx::Rect(15, 15, 20, 20)); - - // Make sure the internal state of DesktopRootWindowHostX11 is set up - // correctly. - ASSERT_EQ(3u, DesktopRootWindowHostX11::GetAllOpenWindows().size()); - - EXPECT_EQ(window_one->GetNativeWindow(), - screen()->GetWindowAtScreenPoint(gfx::Point(15, 15))); - EXPECT_EQ(window_two->GetNativeWindow(), - screen()->GetWindowAtScreenPoint(gfx::Point(55, 55))); - EXPECT_EQ(NULL, - screen()->GetWindowAtScreenPoint(gfx::Point(100, 100))); - - // Bring the third window in front. It overlaps with the first window. - // Hit-testing on the intersecting region should give the third window. - window_three->Activate(); - EXPECT_EQ(window_three->GetNativeWindow(), - screen()->GetWindowAtScreenPoint(gfx::Point(15, 15))); - - window_one->CloseNow(); - window_two->CloseNow(); -} - TEST_F(DesktopScreenX11Test, GetDisplayNearestWindow) { // Set up a two monitor situation. std::vector<gfx::Display> displays; @@ -232,8 +249,10 @@ TEST_F(DesktopScreenX11Test, GetDisplayNearestWindow) { gfx::Rect(640, 0, 1024, 768))); screen()->ProcessDisplayChange(displays); - Widget* window_one = BuildTopLevelDesktopWidget(gfx::Rect(10, 10, 10, 10)); - Widget* window_two = BuildTopLevelDesktopWidget(gfx::Rect(650, 50, 10, 10)); + Widget* window_one = BuildTopLevelDesktopWidget(gfx::Rect(10, 10, 10, 10), + false); + Widget* window_two = BuildTopLevelDesktopWidget(gfx::Rect(650, 50, 10, 10), + false); EXPECT_EQ( kFirstDisplay, @@ -246,4 +265,183 @@ TEST_F(DesktopScreenX11Test, GetDisplayNearestWindow) { window_two->CloseNow(); } +// Tests that the window is maximized in response to a double click event. +TEST_F(DesktopScreenX11Test, DoubleClickHeaderMaximizes) { + if (!ui::WmSupportsHint(ui::GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"))) + return; + + Widget* widget = BuildTopLevelDesktopWidget(gfx::Rect(0, 0, 100, 100), true); + widget->Show(); + TestDesktopNativeWidgetAura* native_widget = + static_cast<TestDesktopNativeWidgetAura*>(widget->native_widget()); + native_widget->set_window_component(HTCAPTION); + + aura::Window* window = widget->GetNativeWindow(); + window->SetProperty(aura::client::kCanMaximizeKey, true); + + // Cast to superclass as DesktopWindowTreeHostX11 hide IsMaximized + DesktopWindowTreeHost* rwh = + DesktopWindowTreeHostX11::GetHostForXID(window->GetHost()-> + GetAcceleratedWidget()); + + aura::test::EventGenerator generator(window); + generator.ClickLeftButton(); + generator.DoubleClickLeftButton(); + RunPendingMessages(); + EXPECT_TRUE(rwh->IsMaximized()); + + widget->CloseNow(); +} + +// Tests that the window does not maximize in response to a double click event, +// if the first click was to a different target component than that of the +// second click. +TEST_F(DesktopScreenX11Test, DoubleClickTwoDifferentTargetsDoesntMaximizes) { + Widget* widget = BuildTopLevelDesktopWidget(gfx::Rect(0, 0, 100, 100), true); + widget->Show(); + TestDesktopNativeWidgetAura* native_widget = + static_cast<TestDesktopNativeWidgetAura*>(widget->native_widget()); + + aura::Window* window = widget->GetNativeWindow(); + window->SetProperty(aura::client::kCanMaximizeKey, true); + + // Cast to superclass as DesktopWindowTreeHostX11 hide IsMaximized + DesktopWindowTreeHost* rwh = + DesktopWindowTreeHostX11::GetHostForXID(window->GetHost()-> + GetAcceleratedWidget()); + + aura::test::EventGenerator generator(window); + native_widget->set_window_component(HTCLIENT); + generator.ClickLeftButton(); + native_widget->set_window_component(HTCAPTION); + generator.DoubleClickLeftButton(); + RunPendingMessages(); + EXPECT_FALSE(rwh->IsMaximized()); + + widget->CloseNow(); +} + +// Tests that the window does not maximize in response to a double click event, +// if the double click was interrupted by a right click. +TEST_F(DesktopScreenX11Test, RightClickDuringDoubleClickDoesntMaximize) { + Widget* widget = BuildTopLevelDesktopWidget(gfx::Rect(0, 0, 100, 100), true); + widget->Show(); + TestDesktopNativeWidgetAura* native_widget = + static_cast<TestDesktopNativeWidgetAura*>(widget->native_widget()); + + aura::Window* window = widget->GetNativeWindow(); + window->SetProperty(aura::client::kCanMaximizeKey, true); + + // Cast to superclass as DesktopWindowTreeHostX11 hide IsMaximized + DesktopWindowTreeHost* rwh = static_cast<DesktopWindowTreeHost*>( + DesktopWindowTreeHostX11::GetHostForXID(window->GetHost()-> + GetAcceleratedWidget())); + + aura::test::EventGenerator generator(window); + native_widget->set_window_component(HTCLIENT); + generator.ClickLeftButton(); + native_widget->set_window_component(HTCAPTION); + generator.PressRightButton(); + generator.ReleaseRightButton(); + EXPECT_FALSE(rwh->IsMaximized()); + generator.DoubleClickLeftButton(); + RunPendingMessages(); + EXPECT_FALSE(rwh->IsMaximized()); + + widget->CloseNow(); +} + +// Test that rotating the displays notifies the DisplayObservers. +TEST_F(DesktopScreenX11Test, RotationChange) { + std::vector<gfx::Display> displays; + displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); + displays.push_back( + gfx::Display(kSecondDisplay, gfx::Rect(640, 0, 1024, 768))); + screen()->ProcessDisplayChange(displays); + ResetDisplayChanges(); + + displays[0].set_rotation(gfx::Display::ROTATE_90); + screen()->ProcessDisplayChange(displays); + EXPECT_EQ(1u, changed_display_.size()); + + displays[1].set_rotation(gfx::Display::ROTATE_90); + screen()->ProcessDisplayChange(displays); + EXPECT_EQ(2u, changed_display_.size()); + + displays[0].set_rotation(gfx::Display::ROTATE_270); + screen()->ProcessDisplayChange(displays); + EXPECT_EQ(3u, changed_display_.size()); + + displays[0].set_rotation(gfx::Display::ROTATE_270); + screen()->ProcessDisplayChange(displays); + EXPECT_EQ(3u, changed_display_.size()); + + displays[0].set_rotation(gfx::Display::ROTATE_0); + displays[1].set_rotation(gfx::Display::ROTATE_0); + screen()->ProcessDisplayChange(displays); + EXPECT_EQ(5u, changed_display_.size()); +} + +// Test that changing the displays workarea notifies the DisplayObservers. +TEST_F(DesktopScreenX11Test, WorkareaChange) { + std::vector<gfx::Display> displays; + displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); + displays.push_back( + gfx::Display(kSecondDisplay, gfx::Rect(640, 0, 1024, 768))); + screen()->ProcessDisplayChange(displays); + ResetDisplayChanges(); + + displays[0].set_work_area(gfx::Rect(0, 0, 300, 300)); + screen()->ProcessDisplayChange(displays); + EXPECT_EQ(1u, changed_display_.size()); + + displays[1].set_work_area(gfx::Rect(0, 0, 300, 300)); + screen()->ProcessDisplayChange(displays); + EXPECT_EQ(2u, changed_display_.size()); + + displays[0].set_work_area(gfx::Rect(0, 0, 300, 300)); + screen()->ProcessDisplayChange(displays); + EXPECT_EQ(2u, changed_display_.size()); + + displays[1].set_work_area(gfx::Rect(0, 0, 300, 300)); + screen()->ProcessDisplayChange(displays); + EXPECT_EQ(2u, changed_display_.size()); + + displays[0].set_work_area(gfx::Rect(0, 0, 640, 480)); + displays[1].set_work_area(gfx::Rect(640, 0, 1024, 768)); + screen()->ProcessDisplayChange(displays); + EXPECT_EQ(4u, changed_display_.size()); +} + +// Test that changing the device scale factor notifies the DisplayObservers. +TEST_F(DesktopScreenX11Test, DeviceScaleFactorChange) { + std::vector<gfx::Display> displays; + displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); + displays.push_back( + gfx::Display(kSecondDisplay, gfx::Rect(640, 0, 1024, 768))); + screen()->ProcessDisplayChange(displays); + ResetDisplayChanges(); + + displays[0].set_device_scale_factor(2.5f); + screen()->ProcessDisplayChange(displays); + EXPECT_EQ(1u, changed_display_.size()); + + displays[1].set_device_scale_factor(2.5f); + screen()->ProcessDisplayChange(displays); + EXPECT_EQ(2u, changed_display_.size()); + + displays[0].set_device_scale_factor(2.5f); + screen()->ProcessDisplayChange(displays); + EXPECT_EQ(2u, changed_display_.size()); + + displays[1].set_device_scale_factor(2.5f); + screen()->ProcessDisplayChange(displays); + EXPECT_EQ(2u, changed_display_.size()); + + displays[0].set_device_scale_factor(1.f); + displays[1].set_device_scale_factor(1.f); + screen()->ProcessDisplayChange(displays); + EXPECT_EQ(4u, changed_display_.size()); +} + } // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_root_window_host.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host.h index c66cee9254c..9ccb67225a1 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_root_window_host.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host.h @@ -2,17 +2,17 @@ // 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_ +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_WINDOW_TREE_HOST_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_WINDOW_TREE_HOST_H_ #include "base/memory/scoped_ptr.h" -#include "ui/aura/root_window.h" +#include "ui/aura/window_event_dispatcher.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 WindowTreeHost; class Window; namespace client { @@ -31,7 +31,6 @@ class NativeTheme; namespace views { namespace corewm { - class Tooltip; } @@ -42,41 +41,39 @@ class NativeWidgetDelegate; class DesktopNativeCursorManager; class DesktopNativeWidgetAura; -class VIEWS_EXPORT DesktopRootWindowHost { +class VIEWS_EXPORT DesktopWindowTreeHost { public: - virtual ~DesktopRootWindowHost() {} + virtual ~DesktopWindowTreeHost() {} - static DesktopRootWindowHost* Create( + static DesktopWindowTreeHost* Create( internal::NativeWidgetDelegate* native_widget_delegate, DesktopNativeWidgetAura* desktop_native_widget_aura); // Return the NativeTheme to use for |window|. WARNING: |window| may be NULL. static ui::NativeTheme* GetNativeTheme(aura::Window* window); - // Sets up resources needed before the RootWindow has been created. + // Sets up resources needed before the WindowEventDispatcher has been created. virtual void Init(aura::Window* content_window, - const Widget::InitParams& params, - aura::RootWindow::CreateParams* rw_create_params) = 0; + const Widget::InitParams& params) = 0; - // Invoked once the RootWindow has been created. Caller owns the RootWindow. - virtual void OnRootWindowCreated(aura::RootWindow* root, - const Widget::InitParams& params) = 0; + // Invoked once the DesktopNativeWidgetAura has been created. + virtual void OnNativeWidgetCreated(const Widget::InitParams& params) = 0; // Creates and returns the Tooltip implementation to use. Return value is // owned by DesktopNativeWidgetAura and lives as long as - // DesktopRootWindowHost. + // DesktopWindowTreeHost. virtual scoped_ptr<corewm::Tooltip> CreateTooltip() = 0; // Creates and returns the DragDropClient implementation to use. Return value // is owned by DesktopNativeWidgetAura and lives as long as - // DesktopRootWindowHost. + // DesktopWindowTreeHost. virtual scoped_ptr<aura::client::DragDropClient> CreateDragDropClient( DesktopNativeCursorManager* cursor_manager) = 0; virtual void Close() = 0; virtual void CloseNow() = 0; - virtual aura::RootWindowHost* AsRootWindowHost() = 0; + virtual aura::WindowTreeHost* AsWindowTreeHost() = 0; virtual void ShowWindowWithState(ui::WindowShowState show_state) = 0; virtual void ShowMaximizedWithBounds(const gfx::Rect& restored_bounds) = 0; @@ -112,8 +109,10 @@ class VIEWS_EXPORT DesktopRootWindowHost { virtual void SetAlwaysOnTop(bool always_on_top) = 0; virtual bool IsAlwaysOnTop() const = 0; + virtual void SetVisibleOnAllWorkspaces(bool always_visible) = 0; + // Returns true if the title changed. - virtual bool SetWindowTitle(const string16& title) = 0; + virtual bool SetWindowTitle(const base::string16& title) = 0; virtual void ClearNativeFocus() = 0; @@ -125,9 +124,12 @@ class VIEWS_EXPORT DesktopRootWindowHost { virtual void SetVisibilityChangedAnimationsEnabled(bool value) = 0; - virtual bool ShouldUseNativeFrame() = 0; + // Determines whether the window should use native title bar and borders. + virtual bool ShouldUseNativeFrame() const = 0; + // Determines whether the window contents should be rendered transparently + // (for example, so that they can overhang onto the window title bar). + virtual bool ShouldWindowContentsBeTransparent() const = 0; virtual void FrameTypeChanged() = 0; - virtual NonClientFrameView* CreateNonClientFrameView() = 0; virtual void SetFullscreen(bool fullscreen) = 0; virtual bool IsFullscreen() const = 0; @@ -151,8 +153,11 @@ class VIEWS_EXPORT DesktopRootWindowHost { // Returns true if the Widget was closed but is still showing because of // animations. virtual bool IsAnimatingClosed() const = 0; + + // Returns true if the Widget supports translucency. + virtual bool IsTranslucentWindowOpacitySupported() const = 0; }; } // namespace views -#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_ROOT_WINDOW_HOST_H_ +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_WINDOW_TREE_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_window_tree_host_observer_x11.h index f430b38c20d..544f20527aa 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_observer_x11.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_observer_x11.h @@ -2,17 +2,17 @@ // 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_ +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_WINDOW_TREE_HOST_OBSERVER_X11_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_WINDOW_TREE_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 { +class VIEWS_EXPORT DesktopWindowTreeHostObserverX11 { public: - virtual ~DesktopRootWindowHostObserverX11() {} + virtual ~DesktopWindowTreeHostObserverX11() {} // Called after we receive a MapNotify event (the X11 server has allocated // resources for it). @@ -25,5 +25,5 @@ class VIEWS_EXPORT DesktopRootWindowHostObserverX11 { } // namespace views -#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_ROOT_WINDOW_HOST_OBSERVER_X11_H_ +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_WINDOW_TREE_HOST_OBSERVER_X11_H_ diff --git a/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_ozone.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_ozone.cc index ffca2a6337c..1a78a9ab57e 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_ozone.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_ozone.cc @@ -3,17 +3,17 @@ // found in the LICENSE file. #include "ui/aura/window_tree_host.h" -#include "ui/views/widget/desktop_aura/desktop_root_window_host.h" #include "ui/views/widget/desktop_aura/desktop_factory_ozone.h" +#include "ui/views/widget/desktop_aura/desktop_window_tree_host.h" namespace views { -DesktopRootWindowHost* DesktopRootWindowHost::Create( +DesktopWindowTreeHost* DesktopWindowTreeHost::Create( internal::NativeWidgetDelegate* native_widget_delegate, DesktopNativeWidgetAura* desktop_native_widget_aura) { DesktopFactoryOzone* d_factory = DesktopFactoryOzone::GetInstance(); - return d_factory->CreateRootWindowHost(native_widget_delegate, + return d_factory->CreateWindowTreeHost(native_widget_delegate, desktop_native_widget_aura); } diff --git a/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc index 938a05b2c4b..63475267815 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_win.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc @@ -2,7 +2,7 @@ // 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 "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h" #include "base/win/metro.h" #include "third_party/skia/include/core/SkPath.h" @@ -10,11 +10,10 @@ #include "ui/aura/client/aura_constants.h" #include "ui/aura/client/cursor_client.h" #include "ui/aura/client/focus_client.h" -#include "ui/aura/root_window.h" +#include "ui/aura/window_event_dispatcher.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/shell.h" #include "ui/compositor/compositor_constants.h" #include "ui/gfx/insets.h" @@ -25,11 +24,7 @@ #include "ui/gfx/win/dpi.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/input_method_event_filter.h" #include "ui/views/corewm/tooltip_win.h" -#include "ui/views/corewm/window_animations.h" #include "ui/views/ime/input_method_bridge.h" #include "ui/views/widget/desktop_aura/desktop_cursor_loader_updater.h" #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h" @@ -40,7 +35,10 @@ #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" +#include "ui/wm/core/compound_event_filter.h" +#include "ui/wm/core/input_method_event_filter.h" +#include "ui/wm/core/window_animations.h" +#include "ui/wm/public/scoped_tooltip_disabler.h" namespace views { @@ -64,18 +62,20 @@ void InsetBottomRight(gfx::Rect* rect, gfx::Vector2d vector) { DEFINE_WINDOW_PROPERTY_KEY(aura::Window*, kContentWindowForRootWindow, NULL); -// Identifies the DesktopRootWindowHostWin associated with the RootWindow. -DEFINE_WINDOW_PROPERTY_KEY(DesktopRootWindowHostWin*, kDesktopRootWindowHostKey, +// Identifies the DesktopWindowTreeHostWin associated with the +// WindowEventDispatcher. +DEFINE_WINDOW_PROPERTY_KEY(DesktopWindowTreeHostWin*, kDesktopWindowTreeHostKey, NULL); //////////////////////////////////////////////////////////////////////////////// -// DesktopRootWindowHostWin, public: +// DesktopWindowTreeHostWin, public: -DesktopRootWindowHostWin::DesktopRootWindowHostWin( +bool DesktopWindowTreeHostWin::is_cursor_visible_ = true; + +DesktopWindowTreeHostWin::DesktopWindowTreeHostWin( internal::NativeWidgetDelegate* native_widget_delegate, DesktopNativeWidgetAura* desktop_native_widget_aura) - : root_window_(NULL), - message_handler_(new HWNDMessageHandler(this)), + : message_handler_(new HWNDMessageHandler(this)), native_widget_delegate_(native_widget_delegate), desktop_native_widget_aura_(desktop_native_widget_aura), content_window_(NULL), @@ -83,32 +83,31 @@ DesktopRootWindowHostWin::DesktopRootWindowHostWin( should_animate_window_close_(false), pending_close_(false), has_non_client_view_(false), - tooltip_(NULL), - is_cursor_visible_(true) { + tooltip_(NULL) { } -DesktopRootWindowHostWin::~DesktopRootWindowHostWin() { +DesktopWindowTreeHostWin::~DesktopWindowTreeHostWin() { // WARNING: |content_window_| has been destroyed by the time we get here. - desktop_native_widget_aura_->OnDesktopRootWindowHostDestroyed( - root_window_); + desktop_native_widget_aura_->OnDesktopWindowTreeHostDestroyed(this); + DestroyDispatcher(); } // static -aura::Window* DesktopRootWindowHostWin::GetContentWindowForHWND(HWND hwnd) { - aura::RootWindow* root = aura::RootWindow::GetForAcceleratedWidget(hwnd); - return root ? root->window()->GetProperty(kContentWindowForRootWindow) : NULL; +aura::Window* DesktopWindowTreeHostWin::GetContentWindowForHWND(HWND hwnd) { + aura::WindowTreeHost* host = + aura::WindowTreeHost::GetForAcceleratedWidget(hwnd); + return host ? host->window()->GetProperty(kContentWindowForRootWindow) : NULL; } // static -ui::NativeTheme* DesktopRootWindowHost::GetNativeTheme(aura::Window* window) { +ui::NativeTheme* DesktopWindowTreeHost::GetNativeTheme(aura::Window* window) { // Use NativeThemeWin for windows shown on the desktop, those not on the // desktop come from Ash and get NativeThemeAura. - aura::WindowEventDispatcher* dispatcher = - window ? window->GetDispatcher() : NULL; - if (dispatcher) { - HWND host_hwnd = dispatcher->host()->GetAcceleratedWidget(); + aura::WindowTreeHost* host = window ? window->GetHost() : NULL; + if (host) { + HWND host_hwnd = host->GetAcceleratedWidget(); if (host_hwnd && - DesktopRootWindowHostWin::GetContentWindowForHWND(host_hwnd)) { + DesktopWindowTreeHostWin::GetContentWindowForHWND(host_hwnd)) { return ui::NativeThemeWin::instance(); } } @@ -116,12 +115,10 @@ ui::NativeTheme* DesktopRootWindowHost::GetNativeTheme(aura::Window* window) { } //////////////////////////////////////////////////////////////////////////////// -// DesktopRootWindowHostWin, DesktopRootWindowHost implementation: +// DesktopWindowTreeHostWin, DesktopWindowTreeHost implementation: -void DesktopRootWindowHostWin::Init( - aura::Window* content_window, - const Widget::InitParams& params, - aura::RootWindow::CreateParams* rw_create_params) { +void DesktopWindowTreeHostWin::Init(aura::Window* content_window, + const Widget::InitParams& params) { // TODO(beng): SetInitParams(). content_window_ = content_window; @@ -132,59 +129,56 @@ void DesktopRootWindowHostWin::Init( native_widget_delegate_); HWND parent_hwnd = NULL; - if (params.parent && params.parent->GetDispatcher()) { - parent_hwnd = - params.parent->GetDispatcher()->host()->GetAcceleratedWidget(); - } + if (params.parent && params.parent->GetHost()) + parent_hwnd = params.parent->GetHost()->GetAcceleratedWidget(); message_handler_->set_remove_standard_frame(params.remove_standard_frame); has_non_client_view_ = Widget::RequiresNonClientView(params.type); + gfx::Rect pixel_bounds = gfx::win::DIPToScreenRect(params.bounds); + message_handler_->Init(parent_hwnd, pixel_bounds); if (params.type == Widget::InitParams::TYPE_MENU) { ::SetProp(GetAcceleratedWidget(), kForceSoftwareCompositor, reinterpret_cast<HANDLE>(true)); } - - gfx::Rect pixel_bounds = gfx::win::DIPToScreenRect(params.bounds); - message_handler_->Init(parent_hwnd, pixel_bounds); - - rw_create_params->host = this; + CreateCompositor(GetAcceleratedWidget()); } -void DesktopRootWindowHostWin::OnRootWindowCreated( - aura::RootWindow* root, +void DesktopWindowTreeHostWin::OnNativeWidgetCreated( const Widget::InitParams& params) { - root_window_ = root; + // The cursor is not necessarily visible when the root window is created. + aura::client::CursorClient* cursor_client = + aura::client::GetCursorClient(window()); + if (cursor_client) + is_cursor_visible_ = cursor_client->IsCursorVisible(); - root_window_->window()->SetProperty(kContentWindowForRootWindow, - content_window_); - root_window_->window()->SetProperty(kDesktopRootWindowHostKey, this); + window()->SetProperty(kContentWindowForRootWindow, content_window_); + window()->SetProperty(kDesktopWindowTreeHostKey, this); should_animate_window_close_ = - content_window_->type() != aura::client::WINDOW_TYPE_NORMAL && - !views::corewm::WindowAnimationsDisabled(content_window_); + content_window_->type() != ui::wm::WINDOW_TYPE_NORMAL && + !wm::WindowAnimationsDisabled(content_window_); // TODO this is not invoked *after* Init(), but should be ok. SetWindowTransparency(); } -scoped_ptr<corewm::Tooltip> DesktopRootWindowHostWin::CreateTooltip() { +scoped_ptr<corewm::Tooltip> DesktopWindowTreeHostWin::CreateTooltip() { DCHECK(!tooltip_); tooltip_ = new corewm::TooltipWin(GetAcceleratedWidget()); return scoped_ptr<corewm::Tooltip>(tooltip_); } scoped_ptr<aura::client::DragDropClient> -DesktopRootWindowHostWin::CreateDragDropClient( +DesktopWindowTreeHostWin::CreateDragDropClient( DesktopNativeCursorManager* cursor_manager) { - drag_drop_client_ = new DesktopDragDropClientWin(root_window_->window(), - GetHWND()); + drag_drop_client_ = new DesktopDragDropClientWin(window(), GetHWND()); return scoped_ptr<aura::client::DragDropClient>(drag_drop_client_).Pass(); } -void DesktopRootWindowHostWin::Close() { +void DesktopWindowTreeHostWin::Close() { // TODO(beng): Move this entire branch to DNWA so it can be shared with X11. if (should_animate_window_close_) { pending_close_ = true; @@ -200,30 +194,30 @@ void DesktopRootWindowHostWin::Close() { } } -void DesktopRootWindowHostWin::CloseNow() { +void DesktopWindowTreeHostWin::CloseNow() { message_handler_->CloseNow(); } -aura::RootWindowHost* DesktopRootWindowHostWin::AsRootWindowHost() { +aura::WindowTreeHost* DesktopWindowTreeHostWin::AsWindowTreeHost() { return this; } -void DesktopRootWindowHostWin::ShowWindowWithState( +void DesktopWindowTreeHostWin::ShowWindowWithState( ui::WindowShowState show_state) { message_handler_->ShowWindowWithState(show_state); } -void DesktopRootWindowHostWin::ShowMaximizedWithBounds( +void DesktopWindowTreeHostWin::ShowMaximizedWithBounds( const gfx::Rect& restored_bounds) { gfx::Rect pixel_bounds = gfx::win::DIPToScreenRect(restored_bounds); message_handler_->ShowMaximizedWithBounds(pixel_bounds); } -bool DesktopRootWindowHostWin::IsVisible() const { +bool DesktopWindowTreeHostWin::IsVisible() const { return message_handler_->IsVisible(); } -void DesktopRootWindowHostWin::SetSize(const gfx::Size& size) { +void DesktopWindowTreeHostWin::SetSize(const gfx::Size& size) { gfx::Size size_in_pixels = gfx::win::DIPToScreenSize(size); gfx::Size expanded = GetExpandedWindowSize( message_handler_->window_ex_style(), size_in_pixels); @@ -233,11 +227,11 @@ void DesktopRootWindowHostWin::SetSize(const gfx::Size& size) { message_handler_->SetSize(expanded); } -void DesktopRootWindowHostWin::StackAtTop() { +void DesktopWindowTreeHostWin::StackAtTop() { message_handler_->StackAtTop(); } -void DesktopRootWindowHostWin::CenterWindow(const gfx::Size& size) { +void DesktopWindowTreeHostWin::CenterWindow(const gfx::Size& size) { gfx::Size size_in_pixels = gfx::win::DIPToScreenSize(size); gfx::Size expanded_size; expanded_size = GetExpandedWindowSize(message_handler_->window_ex_style(), @@ -248,7 +242,7 @@ void DesktopRootWindowHostWin::CenterWindow(const gfx::Size& size) { message_handler_->CenterWindow(expanded_size); } -void DesktopRootWindowHostWin::GetWindowPlacement( +void DesktopWindowTreeHostWin::GetWindowPlacement( gfx::Rect* bounds, ui::WindowShowState* show_state) const { message_handler_->GetWindowPlacement(bounds, show_state); @@ -256,25 +250,25 @@ void DesktopRootWindowHostWin::GetWindowPlacement( *bounds = gfx::win::ScreenToDIPRect(*bounds); } -gfx::Rect DesktopRootWindowHostWin::GetWindowBoundsInScreen() const { +gfx::Rect DesktopWindowTreeHostWin::GetWindowBoundsInScreen() const { gfx::Rect pixel_bounds = message_handler_->GetWindowBoundsInScreen(); InsetBottomRight(&pixel_bounds, window_enlargement_); return gfx::win::ScreenToDIPRect(pixel_bounds); } -gfx::Rect DesktopRootWindowHostWin::GetClientAreaBoundsInScreen() const { +gfx::Rect DesktopWindowTreeHostWin::GetClientAreaBoundsInScreen() const { gfx::Rect pixel_bounds = message_handler_->GetClientAreaBoundsInScreen(); InsetBottomRight(&pixel_bounds, window_enlargement_); return gfx::win::ScreenToDIPRect(pixel_bounds); } -gfx::Rect DesktopRootWindowHostWin::GetRestoredBounds() const { +gfx::Rect DesktopWindowTreeHostWin::GetRestoredBounds() const { gfx::Rect pixel_bounds = message_handler_->GetRestoredBounds(); InsetBottomRight(&pixel_bounds, window_enlargement_); return gfx::win::ScreenToDIPRect(pixel_bounds); } -gfx::Rect DesktopRootWindowHostWin::GetWorkAreaBoundsInScreen() const { +gfx::Rect DesktopWindowTreeHostWin::GetWorkAreaBoundsInScreen() const { MONITORINFO monitor_info; monitor_info.cbSize = sizeof(monitor_info); GetMonitorInfo(MonitorFromWindow(message_handler_->hwnd(), @@ -284,7 +278,7 @@ gfx::Rect DesktopRootWindowHostWin::GetWorkAreaBoundsInScreen() const { return gfx::win::ScreenToDIPRect(pixel_bounds); } -void DesktopRootWindowHostWin::SetShape(gfx::NativeRegion native_region) { +void DesktopWindowTreeHostWin::SetShape(gfx::NativeRegion native_region) { if (native_region) { message_handler_->SetRegion(gfx::CreateHRGNFromSkRegion(*native_region)); } else { @@ -294,59 +288,63 @@ void DesktopRootWindowHostWin::SetShape(gfx::NativeRegion native_region) { delete native_region; } -void DesktopRootWindowHostWin::Activate() { +void DesktopWindowTreeHostWin::Activate() { message_handler_->Activate(); } -void DesktopRootWindowHostWin::Deactivate() { +void DesktopWindowTreeHostWin::Deactivate() { message_handler_->Deactivate(); } -bool DesktopRootWindowHostWin::IsActive() const { +bool DesktopWindowTreeHostWin::IsActive() const { return message_handler_->IsActive(); } -void DesktopRootWindowHostWin::Maximize() { +void DesktopWindowTreeHostWin::Maximize() { message_handler_->Maximize(); } -void DesktopRootWindowHostWin::Minimize() { +void DesktopWindowTreeHostWin::Minimize() { message_handler_->Minimize(); } -void DesktopRootWindowHostWin::Restore() { +void DesktopWindowTreeHostWin::Restore() { message_handler_->Restore(); } -bool DesktopRootWindowHostWin::IsMaximized() const { +bool DesktopWindowTreeHostWin::IsMaximized() const { return message_handler_->IsMaximized(); } -bool DesktopRootWindowHostWin::IsMinimized() const { +bool DesktopWindowTreeHostWin::IsMinimized() const { return message_handler_->IsMinimized(); } -bool DesktopRootWindowHostWin::HasCapture() const { +bool DesktopWindowTreeHostWin::HasCapture() const { return message_handler_->HasCapture(); } -void DesktopRootWindowHostWin::SetAlwaysOnTop(bool always_on_top) { +void DesktopWindowTreeHostWin::SetAlwaysOnTop(bool always_on_top) { message_handler_->SetAlwaysOnTop(always_on_top); } -bool DesktopRootWindowHostWin::IsAlwaysOnTop() const { +bool DesktopWindowTreeHostWin::IsAlwaysOnTop() const { return message_handler_->IsAlwaysOnTop(); } -bool DesktopRootWindowHostWin::SetWindowTitle(const string16& title) { +void DesktopWindowTreeHostWin::SetVisibleOnAllWorkspaces(bool always_visible) { + // Windows does not have the concept of workspaces. +} + +bool DesktopWindowTreeHostWin::SetWindowTitle(const base::string16& title) { return message_handler_->SetTitle(title); } -void DesktopRootWindowHostWin::ClearNativeFocus() { +void DesktopWindowTreeHostWin::ClearNativeFocus() { message_handler_->ClearNativeFocus(); } -Widget::MoveLoopResult DesktopRootWindowHostWin::RunMoveLoop( +Widget::MoveLoopResult DesktopWindowTreeHostWin::RunMoveLoop( const gfx::Vector2d& drag_offset, Widget::MoveLoopSource source, Widget::MoveLoopEscapeBehavior escape_behavior) { @@ -356,31 +354,34 @@ Widget::MoveLoopResult DesktopRootWindowHostWin::RunMoveLoop( Widget::MOVE_LOOP_SUCCESSFUL : Widget::MOVE_LOOP_CANCELED; } -void DesktopRootWindowHostWin::EndMoveLoop() { +void DesktopWindowTreeHostWin::EndMoveLoop() { message_handler_->EndMoveLoop(); } -void DesktopRootWindowHostWin::SetVisibilityChangedAnimationsEnabled( +void DesktopWindowTreeHostWin::SetVisibilityChangedAnimationsEnabled( bool value) { message_handler_->SetVisibilityChangedAnimationsEnabled(value); content_window_->SetProperty(aura::client::kAnimationsDisabledKey, !value); } -bool DesktopRootWindowHostWin::ShouldUseNativeFrame() { - return ui::win::IsAeroGlassEnabled(); +bool DesktopWindowTreeHostWin::ShouldUseNativeFrame() const { + return IsTranslucentWindowOpacitySupported(); } -void DesktopRootWindowHostWin::FrameTypeChanged() { - message_handler_->FrameTypeChanged(); - SetWindowTransparency(); +bool DesktopWindowTreeHostWin::ShouldWindowContentsBeTransparent() const { + // If the window has a native frame, we assume it is an Aero Glass window, and + // is therefore transparent. Note: This is not equivalent to calling + // IsAeroGlassEnabled, because ShouldUseNativeFrame is overridden in a + // subclass. + return ShouldUseNativeFrame(); } -NonClientFrameView* DesktopRootWindowHostWin::CreateNonClientFrameView() { - return GetWidget()->ShouldUseNativeFrame() ? - new NativeFrameView(GetWidget()) : NULL; +void DesktopWindowTreeHostWin::FrameTypeChanged() { + message_handler_->FrameTypeChanged(); + SetWindowTransparency(); } -void DesktopRootWindowHostWin::SetFullscreen(bool fullscreen) { +void DesktopWindowTreeHostWin::SetFullscreen(bool fullscreen) { message_handler_->fullscreen_handler()->SetFullscreen(fullscreen); // TODO(sky): workaround for ScopedFullscreenVisibility showing window // directly. Instead of this should listen for visibility changes and then @@ -390,70 +391,70 @@ void DesktopRootWindowHostWin::SetFullscreen(bool fullscreen) { SetWindowTransparency(); } -bool DesktopRootWindowHostWin::IsFullscreen() const { +bool DesktopWindowTreeHostWin::IsFullscreen() const { return message_handler_->fullscreen_handler()->fullscreen(); } -void DesktopRootWindowHostWin::SetOpacity(unsigned char opacity) { +void DesktopWindowTreeHostWin::SetOpacity(unsigned char opacity) { message_handler_->SetOpacity(static_cast<BYTE>(opacity)); content_window_->layer()->SetOpacity(opacity / 255.0); } -void DesktopRootWindowHostWin::SetWindowIcons( +void DesktopWindowTreeHostWin::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) { +void DesktopWindowTreeHostWin::InitModalType(ui::ModalType modal_type) { message_handler_->InitModalType(modal_type); } -void DesktopRootWindowHostWin::FlashFrame(bool flash_frame) { +void DesktopWindowTreeHostWin::FlashFrame(bool flash_frame) { message_handler_->FlashFrame(flash_frame); } -void DesktopRootWindowHostWin::OnRootViewLayout() const { +void DesktopWindowTreeHostWin::OnRootViewLayout() const { } -void DesktopRootWindowHostWin::OnNativeWidgetFocus() { +void DesktopWindowTreeHostWin::OnNativeWidgetFocus() { // HWNDMessageHandler will perform the proper updating on its own. } -void DesktopRootWindowHostWin::OnNativeWidgetBlur() { +void DesktopWindowTreeHostWin::OnNativeWidgetBlur() { } -bool DesktopRootWindowHostWin::IsAnimatingClosed() const { +bool DesktopWindowTreeHostWin::IsAnimatingClosed() const { return pending_close_; } +bool DesktopWindowTreeHostWin::IsTranslucentWindowOpacitySupported() const { + return ui::win::IsAeroGlassEnabled(); +} + //////////////////////////////////////////////////////////////////////////////// -// DesktopRootWindowHostWin, RootWindowHost implementation: +// DesktopWindowTreeHostWin, WindowTreeHost implementation: -aura::RootWindow* DesktopRootWindowHostWin::GetRootWindow() { - return root_window_; +ui::EventSource* DesktopWindowTreeHostWin::GetEventSource() { + return this; } -gfx::AcceleratedWidget DesktopRootWindowHostWin::GetAcceleratedWidget() { +gfx::AcceleratedWidget DesktopWindowTreeHostWin::GetAcceleratedWidget() { return message_handler_->hwnd(); } -void DesktopRootWindowHostWin::Show() { +void DesktopWindowTreeHostWin::Show() { message_handler_->Show(); } -void DesktopRootWindowHostWin::Hide() { +void DesktopWindowTreeHostWin::Hide() { if (!pending_close_) message_handler_->Hide(); } -void DesktopRootWindowHostWin::ToggleFullScreen() { - SetWindowTransparency(); -} - // GetBounds and SetBounds work in pixel coordinates, whereas other get/set // methods work in DIP. -gfx::Rect DesktopRootWindowHostWin::GetBounds() const { +gfx::Rect DesktopWindowTreeHostWin::GetBounds() const { gfx::Rect bounds(message_handler_->GetClientAreaBounds()); // If the window bounds were expanded we need to return the original bounds // To achieve this we do the reverse of the expansion, i.e. add the @@ -469,7 +470,7 @@ gfx::Rect DesktopRootWindowHostWin::GetBounds() const { return without_expansion; } -void DesktopRootWindowHostWin::SetBounds(const gfx::Rect& bounds) { +void DesktopWindowTreeHostWin::SetBounds(const gfx::Rect& bounds) { // If the window bounds have to be expanded we need to subtract the // window_expansion_top_left_delta_ from the origin and add the // window_expansion_bottom_right_delta_ to the width and height @@ -489,92 +490,60 @@ void DesktopRootWindowHostWin::SetBounds(const gfx::Rect& bounds) { window_enlargement_ = gfx::Vector2d(new_expanded.width() - expanded.width(), new_expanded.height() - expanded.height()); - message_handler_->SetBounds(new_expanded); - - // The client area size may have changed even though the window bounds have - // not, if the window bounds were expanded to 64 pixels both times. - if (old_hwnd_size == new_expanded.size() && old_content_size != bounds.size()) - HandleClientSizeChanged(new_expanded.size()); -} - -gfx::Insets DesktopRootWindowHostWin::GetInsets() const { - return gfx::Insets(); -} - -void DesktopRootWindowHostWin::SetInsets(const gfx::Insets& insets) { + message_handler_->SetBounds(new_expanded, old_content_size != bounds.size()); } -gfx::Point DesktopRootWindowHostWin::GetLocationOnNativeScreen() const { +gfx::Point DesktopWindowTreeHostWin::GetLocationOnNativeScreen() const { return GetBounds().origin(); } -void DesktopRootWindowHostWin::SetCapture() { +void DesktopWindowTreeHostWin::SetCapture() { message_handler_->SetCapture(); } -void DesktopRootWindowHostWin::ReleaseCapture() { +void DesktopWindowTreeHostWin::ReleaseCapture() { message_handler_->ReleaseCapture(); } -void DesktopRootWindowHostWin::SetCursor(gfx::NativeCursor cursor) { - ui::CursorLoaderWin cursor_loader; - cursor_loader.SetPlatformCursor(&cursor); - - message_handler_->SetCursor(cursor.platform()); +void DesktopWindowTreeHostWin::PostNativeEvent( + const base::NativeEvent& native_event) { } -bool DesktopRootWindowHostWin::QueryMouseLocation(gfx::Point* location_return) { - aura::client::CursorClient* cursor_client = - aura::client::GetCursorClient(root_window_->window()); - 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; +void DesktopWindowTreeHostWin::OnDeviceScaleFactorChanged( + float device_scale_factor) { } -bool DesktopRootWindowHostWin::ConfineCursorToRootWindow() { - RECT window_rect = root_window_->window()->GetBoundsInScreen().ToRECT(); - ::ClipCursor(&window_rect); - return true; -} +void DesktopWindowTreeHostWin::SetCursorNative(gfx::NativeCursor cursor) { + ui::CursorLoaderWin cursor_loader; + cursor_loader.SetPlatformCursor(&cursor); -void DesktopRootWindowHostWin::UnConfineCursor() { - ::ClipCursor(NULL); + message_handler_->SetCursor(cursor.platform()); } -void DesktopRootWindowHostWin::OnCursorVisibilityChanged(bool show) { +void DesktopWindowTreeHostWin::OnCursorVisibilityChangedNative(bool show) { if (is_cursor_visible_ == show) return; is_cursor_visible_ = show; ::ShowCursor(!!show); } -void DesktopRootWindowHostWin::MoveCursorTo(const gfx::Point& location) { +void DesktopWindowTreeHostWin::MoveCursorToNative(const gfx::Point& location) { POINT cursor_location = location.ToPOINT(); ::ClientToScreen(GetHWND(), &cursor_location); ::SetCursorPos(cursor_location.x, cursor_location.y); } -void DesktopRootWindowHostWin::PostNativeEvent( - const base::NativeEvent& native_event) { -} - -void DesktopRootWindowHostWin::OnDeviceScaleFactorChanged( - float device_scale_factor) { -} +//////////////////////////////////////////////////////////////////////////////// +// DesktopWindowTreeHostWin, ui::EventSource implementation: -void DesktopRootWindowHostWin::PrepareForShutdown() { +ui::EventProcessor* DesktopWindowTreeHostWin::GetEventProcessor() { + return dispatcher(); } //////////////////////////////////////////////////////////////////////////////// -// DesktopRootWindowHostWin, aura::AnimationHost implementation: +// DesktopWindowTreeHostWin, aura::AnimationHost implementation: -void DesktopRootWindowHostWin::SetHostTransitionOffsets( +void DesktopWindowTreeHostWin::SetHostTransitionOffsets( const gfx::Vector2d& top_left_delta, const gfx::Vector2d& bottom_right_delta) { gfx::Rect bounds_without_expansion = GetBounds(); @@ -583,88 +552,72 @@ void DesktopRootWindowHostWin::SetHostTransitionOffsets( SetBounds(bounds_without_expansion); } -void DesktopRootWindowHostWin::OnWindowHidingAnimationCompleted() { +void DesktopWindowTreeHostWin::OnWindowHidingAnimationCompleted() { if (pending_close_) message_handler_->Close(); } //////////////////////////////////////////////////////////////////////////////// -// DesktopRootWindowHostWin, HWNDMessageHandlerDelegate implementation: +// DesktopWindowTreeHostWin, HWNDMessageHandlerDelegate implementation: -bool DesktopRootWindowHostWin::IsWidgetWindow() const { +bool DesktopWindowTreeHostWin::IsWidgetWindow() const { return has_non_client_view_; } -bool DesktopRootWindowHostWin::IsUsingCustomFrame() const { +bool DesktopWindowTreeHostWin::IsUsingCustomFrame() const { return !GetWidget()->ShouldUseNativeFrame(); } -void DesktopRootWindowHostWin::SchedulePaint() { +void DesktopWindowTreeHostWin::SchedulePaint() { GetWidget()->GetRootView()->SchedulePaint(); } -void DesktopRootWindowHostWin::EnableInactiveRendering() { +void DesktopWindowTreeHostWin::EnableInactiveRendering() { native_widget_delegate_->EnableInactiveRendering(); } -bool DesktopRootWindowHostWin::IsInactiveRenderingDisabled() { +bool DesktopWindowTreeHostWin::IsInactiveRenderingDisabled() { return native_widget_delegate_->IsInactiveRenderingDisabled(); } -bool DesktopRootWindowHostWin::CanResize() const { +bool DesktopWindowTreeHostWin::CanResize() const { return GetWidget()->widget_delegate()->CanResize(); } -bool DesktopRootWindowHostWin::CanMaximize() const { +bool DesktopWindowTreeHostWin::CanMaximize() const { return GetWidget()->widget_delegate()->CanMaximize(); } -bool DesktopRootWindowHostWin::CanActivate() const { +bool DesktopWindowTreeHostWin::CanActivate() const { if (IsModalWindowActive()) return true; return native_widget_delegate_->CanActivate(); } -bool DesktopRootWindowHostWin::WidgetSizeIsClientSize() const { +bool DesktopWindowTreeHostWin::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 { +bool DesktopWindowTreeHostWin::IsModal() const { return native_widget_delegate_->IsModal(); } -int DesktopRootWindowHostWin::GetInitialShowState() const { - return SW_SHOWNORMAL; +int DesktopWindowTreeHostWin::GetInitialShowState() const { + return CanActivate() ? SW_SHOWNORMAL : SW_SHOWNOACTIVATE; } -bool DesktopRootWindowHostWin::WillProcessWorkAreaChange() const { +bool DesktopWindowTreeHostWin::WillProcessWorkAreaChange() const { return GetWidget()->widget_delegate()->WillProcessWorkAreaChange(); } -int DesktopRootWindowHostWin::GetNonClientComponent( +int DesktopWindowTreeHostWin::GetNonClientComponent( const gfx::Point& point) const { gfx::Point dip_position = gfx::win::ScreenToDIPPoint(point); return native_widget_delegate_->GetNonClientComponent(dip_position); } -void DesktopRootWindowHostWin::GetWindowMask(const gfx::Size& size, +void DesktopWindowTreeHostWin::GetWindowMask(const gfx::Size& size, gfx::Path* path) { if (GetWidget()->non_client_view()) { GetWidget()->non_client_view()->GetWindowMask(size, path); @@ -677,193 +630,192 @@ void DesktopRootWindowHostWin::GetWindowMask(const gfx::Size& size, } } -bool DesktopRootWindowHostWin::GetClientAreaInsets(gfx::Insets* insets) const { +bool DesktopWindowTreeHostWin::GetClientAreaInsets(gfx::Insets* insets) const { return false; } -void DesktopRootWindowHostWin::GetMinMaxSize(gfx::Size* min_size, +void DesktopWindowTreeHostWin::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 { +gfx::Size DesktopWindowTreeHostWin::GetRootViewSize() const { return GetWidget()->GetRootView()->size(); } -void DesktopRootWindowHostWin::ResetWindowControls() { +void DesktopWindowTreeHostWin::ResetWindowControls() { GetWidget()->non_client_view()->ResetWindowControls(); } -void DesktopRootWindowHostWin::PaintLayeredWindow(gfx::Canvas* canvas) { - GetWidget()->GetRootView()->Paint(canvas); +void DesktopWindowTreeHostWin::PaintLayeredWindow(gfx::Canvas* canvas) { + GetWidget()->GetRootView()->Paint(canvas, views::CullSet()); } -gfx::NativeViewAccessible DesktopRootWindowHostWin::GetNativeViewAccessible() { +gfx::NativeViewAccessible DesktopWindowTreeHostWin::GetNativeViewAccessible() { return GetWidget()->GetRootView()->GetNativeViewAccessible(); } -InputMethod* DesktopRootWindowHostWin::GetInputMethod() { +InputMethod* DesktopWindowTreeHostWin::GetInputMethod() { return GetWidget()->GetInputMethodDirect(); } -bool DesktopRootWindowHostWin::ShouldHandleSystemCommands() const { +bool DesktopWindowTreeHostWin::ShouldHandleSystemCommands() const { return GetWidget()->widget_delegate()->ShouldHandleSystemCommands(); } -void DesktopRootWindowHostWin::HandleAppDeactivated() { +void DesktopWindowTreeHostWin::HandleAppDeactivated() { native_widget_delegate_->EnableInactiveRendering(); } -void DesktopRootWindowHostWin::HandleActivationChanged(bool active) { +void DesktopWindowTreeHostWin::HandleActivationChanged(bool active) { // This can be invoked from HWNDMessageHandler::Init(), at which point we're // not in a good state and need to ignore it. - if (!delegate_) + // TODO(beng): Do we need this still now the host owns the dispatcher? + if (!dispatcher()) return; if (active) - delegate_->OnHostActivated(); + OnHostActivated(); desktop_native_widget_aura_->HandleActivationChanged(active); } -bool DesktopRootWindowHostWin::HandleAppCommand(short command) { +bool DesktopWindowTreeHostWin::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() { - delegate_->OnHostCancelMode(); +void DesktopWindowTreeHostWin::HandleCancelMode() { + dispatcher()->DispatchCancelModeEvent(); } -void DesktopRootWindowHostWin::HandleCaptureLost() { - delegate_->OnHostLostWindowCapture(); +void DesktopWindowTreeHostWin::HandleCaptureLost() { + OnHostLostWindowCapture(); native_widget_delegate_->OnMouseCaptureLost(); } -void DesktopRootWindowHostWin::HandleClose() { +void DesktopWindowTreeHostWin::HandleClose() { GetWidget()->Close(); } -bool DesktopRootWindowHostWin::HandleCommand(int command) { +bool DesktopWindowTreeHostWin::HandleCommand(int command) { return GetWidget()->widget_delegate()->ExecuteWindowsCommand(command); } -void DesktopRootWindowHostWin::HandleAccelerator( +void DesktopWindowTreeHostWin::HandleAccelerator( const ui::Accelerator& accelerator) { GetWidget()->GetFocusManager()->ProcessAccelerator(accelerator); } -void DesktopRootWindowHostWin::HandleCreate() { - // TODO(beng): moar - NOTIMPLEMENTED(); - +void DesktopWindowTreeHostWin::HandleCreate() { native_widget_delegate_->OnNativeWidgetCreated(true); - - // 1. Window property association - // 2. MouseWheel. } -void DesktopRootWindowHostWin::HandleDestroying() { +void DesktopWindowTreeHostWin::HandleDestroying() { drag_drop_client_->OnNativeWidgetDestroying(GetHWND()); native_widget_delegate_->OnNativeWidgetDestroying(); + + // Destroy the compositor before destroying the HWND since shutdown + // may try to swap to the window. + DestroyCompositor(); } -void DesktopRootWindowHostWin::HandleDestroyed() { +void DesktopWindowTreeHostWin::HandleDestroyed() { desktop_native_widget_aura_->OnHostClosed(); } -bool DesktopRootWindowHostWin::HandleInitialFocus() { - return GetWidget()->SetInitialFocus(); +bool DesktopWindowTreeHostWin::HandleInitialFocus( + ui::WindowShowState show_state) { + return GetWidget()->SetInitialFocus(show_state); } -void DesktopRootWindowHostWin::HandleDisplayChange() { +void DesktopWindowTreeHostWin::HandleDisplayChange() { GetWidget()->widget_delegate()->OnDisplayChanged(); } -void DesktopRootWindowHostWin::HandleBeginWMSizeMove() { +void DesktopWindowTreeHostWin::HandleBeginWMSizeMove() { native_widget_delegate_->OnNativeWidgetBeginUserBoundsChange(); } -void DesktopRootWindowHostWin::HandleEndWMSizeMove() { +void DesktopWindowTreeHostWin::HandleEndWMSizeMove() { native_widget_delegate_->OnNativeWidgetEndUserBoundsChange(); } -void DesktopRootWindowHostWin::HandleMove() { +void DesktopWindowTreeHostWin::HandleMove() { native_widget_delegate_->OnNativeWidgetMove(); - if (delegate_) - delegate_->OnHostMoved(GetBounds().origin()); + OnHostMoved(GetBounds().origin()); } -void DesktopRootWindowHostWin::HandleWorkAreaChanged() { +void DesktopWindowTreeHostWin::HandleWorkAreaChanged() { GetWidget()->widget_delegate()->OnWorkAreaChanged(); } -void DesktopRootWindowHostWin::HandleVisibilityChanging(bool visible) { +void DesktopWindowTreeHostWin::HandleVisibilityChanging(bool visible) { native_widget_delegate_->OnNativeWidgetVisibilityChanging(visible); } -void DesktopRootWindowHostWin::HandleVisibilityChanged(bool visible) { +void DesktopWindowTreeHostWin::HandleVisibilityChanged(bool visible) { native_widget_delegate_->OnNativeWidgetVisibilityChanged(visible); } -void DesktopRootWindowHostWin::HandleClientSizeChanged( +void DesktopWindowTreeHostWin::HandleClientSizeChanged( const gfx::Size& new_size) { - if (delegate_) - delegate_->OnHostResized(new_size); + if (dispatcher()) + OnHostResized(new_size); } -void DesktopRootWindowHostWin::HandleFrameChanged() { +void DesktopWindowTreeHostWin::HandleFrameChanged() { SetWindowTransparency(); // Replace the frame and layout the contents. GetWidget()->non_client_view()->UpdateFrame(); } -void DesktopRootWindowHostWin::HandleNativeFocus(HWND last_focused_window) { +void DesktopWindowTreeHostWin::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) { +void DesktopWindowTreeHostWin::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 delegate_->OnHostMouseEvent(const_cast<ui::MouseEvent*>(&event)); +bool DesktopWindowTreeHostWin::HandleMouseEvent(const ui::MouseEvent& event) { + SendEventToProcessor(const_cast<ui::MouseEvent*>(&event)); + return event.handled(); } -bool DesktopRootWindowHostWin::HandleKeyEvent(const ui::KeyEvent& event) { +bool DesktopWindowTreeHostWin::HandleKeyEvent(const ui::KeyEvent& event) { return false; } -bool DesktopRootWindowHostWin::HandleUntranslatedKeyEvent( +bool DesktopWindowTreeHostWin::HandleUntranslatedKeyEvent( const ui::KeyEvent& event) { ui::KeyEvent duplicate_event(event); - return delegate_->OnHostKeyEvent(&duplicate_event); + SendEventToProcessor(&duplicate_event); + return duplicate_event.handled(); } -void DesktopRootWindowHostWin::HandleTouchEvent( +void DesktopWindowTreeHostWin::HandleTouchEvent( const ui::TouchEvent& event) { // HWNDMessageHandler asynchronously processes touch events. Because of this - // it's possible for the aura::RootWindow to have been destroyed by the time - // we attempt to process them. + // it's possible for the aura::WindowEventDispatcher to have been destroyed + // by the time we attempt to process them. if (!GetWidget()->GetNativeView()) return; // Currently we assume the window that has capture gets touch events too. - aura::RootWindow* root = - aura::RootWindow::GetForAcceleratedWidget(GetCapture()); - if (root) { - DesktopRootWindowHostWin* target = - root->window()->GetProperty(kDesktopRootWindowHostKey); + aura::WindowTreeHost* host = + aura::WindowTreeHost::GetForAcceleratedWidget(GetCapture()); + if (host) { + DesktopWindowTreeHostWin* target = + host->window()->GetProperty(kDesktopWindowTreeHostKey); if (target && target->HasCapture() && target != this) { POINT target_location(event.location().ToPOINT()); ClientToScreen(GetHWND(), &target_location); @@ -872,15 +824,14 @@ void DesktopRootWindowHostWin::HandleTouchEvent( static_cast<View*>(NULL)); target_event.set_location(gfx::Point(target_location)); target_event.set_root_location(target_event.location()); - target->delegate_->OnHostTouchEvent(&target_event); + target->SendEventToProcessor(&target_event); return; } } - delegate_->OnHostTouchEvent( - const_cast<ui::TouchEvent*>(&event)); + SendEventToProcessor(const_cast<ui::TouchEvent*>(&event)); } -bool DesktopRootWindowHostWin::HandleIMEMessage(UINT message, +bool DesktopWindowTreeHostWin::HandleIMEMessage(UINT message, WPARAM w_param, LPARAM l_param, LRESULT* result) { @@ -893,83 +844,100 @@ bool DesktopRootWindowHostWin::HandleIMEMessage(UINT message, input_method()->OnUntranslatedIMEMessage(msg, result); } -void DesktopRootWindowHostWin::HandleInputLanguageChange( +void DesktopWindowTreeHostWin::HandleInputLanguageChange( DWORD character_set, HKL input_language_id) { desktop_native_widget_aura_->input_method_event_filter()-> input_method()->OnInputLocaleChanged(); } -bool DesktopRootWindowHostWin::HandlePaintAccelerated( +bool DesktopWindowTreeHostWin::HandlePaintAccelerated( const gfx::Rect& invalid_rect) { return native_widget_delegate_->OnNativeWidgetPaintAccelerated(invalid_rect); } -void DesktopRootWindowHostWin::HandlePaint(gfx::Canvas* canvas) { - delegate_->OnHostPaint(gfx::Rect()); +void DesktopWindowTreeHostWin::HandlePaint(gfx::Canvas* canvas) { + // It appears possible to get WM_PAINT after WM_DESTROY. + if (compositor()) + compositor()->ScheduleRedrawRect(gfx::Rect()); } -bool DesktopRootWindowHostWin::HandleTooltipNotify(int w_param, +bool DesktopWindowTreeHostWin::HandleTooltipNotify(int w_param, NMHDR* l_param, LRESULT* l_result) { return tooltip_ && tooltip_->HandleNotify(w_param, l_param, l_result); } -void DesktopRootWindowHostWin::HandleTooltipMouseMove(UINT message, +void DesktopWindowTreeHostWin::HandleTooltipMouseMove(UINT message, WPARAM w_param, LPARAM l_param) { // TooltipWin implementation doesn't need this. // TODO(sky): remove from HWNDMessageHandler once non-aura path nuked. } -bool DesktopRootWindowHostWin::PreHandleMSG(UINT message, +void DesktopWindowTreeHostWin::HandleMenuLoop(bool in_menu_loop) { + if (in_menu_loop) { + tooltip_disabler_.reset( + new aura::client::ScopedTooltipDisabler(window())); + } else { + tooltip_disabler_.reset(); + } +} + +bool DesktopWindowTreeHostWin::PreHandleMSG(UINT message, WPARAM w_param, LPARAM l_param, LRESULT* result) { return false; } -void DesktopRootWindowHostWin::PostHandleMSG(UINT message, +void DesktopWindowTreeHostWin::PostHandleMSG(UINT message, WPARAM w_param, LPARAM l_param) { } -bool DesktopRootWindowHostWin::HandleScrollEvent( +bool DesktopWindowTreeHostWin::HandleScrollEvent( const ui::ScrollEvent& event) { - return delegate_->OnHostScrollEvent(const_cast<ui::ScrollEvent*>(&event)); + SendEventToProcessor(const_cast<ui::ScrollEvent*>(&event)); + return event.handled(); +} + +void DesktopWindowTreeHostWin::HandleWindowSizeChanging() { + if (compositor()) + compositor()->FinishAllRendering(); } //////////////////////////////////////////////////////////////////////////////// -// DesktopRootWindowHostWin, private: +// DesktopWindowTreeHostWin, private: -Widget* DesktopRootWindowHostWin::GetWidget() { +Widget* DesktopWindowTreeHostWin::GetWidget() { return native_widget_delegate_->AsWidget(); } -const Widget* DesktopRootWindowHostWin::GetWidget() const { +const Widget* DesktopWindowTreeHostWin::GetWidget() const { return native_widget_delegate_->AsWidget(); } -HWND DesktopRootWindowHostWin::GetHWND() const { +HWND DesktopWindowTreeHostWin::GetHWND() const { return message_handler_->hwnd(); } -void DesktopRootWindowHostWin::SetWindowTransparency() { +void DesktopWindowTreeHostWin::SetWindowTransparency() { bool transparent = ShouldUseNativeFrame() && !IsFullscreen(); - root_window_->compositor()->SetHostHasTransparentBackground(transparent); - root_window_->window()->SetTransparent(transparent); + compositor()->SetHostHasTransparentBackground(transparent); + window()->SetTransparent(transparent); content_window_->SetTransparent(transparent); } -bool DesktopRootWindowHostWin::IsModalWindowActive() const { +bool DesktopWindowTreeHostWin::IsModalWindowActive() const { // This function can get called during window creation which occurs before - // root_window_ has been created. - if (!root_window_) + // dispatcher() has been created. + if (!dispatcher()) return false; aura::Window::Windows::const_iterator index; - for (index = root_window_->window()->children().begin(); - index != root_window_->window()->children().end(); + for (index = window()->children().begin(); + index != window()->children().end(); ++index) { if ((*index)->GetProperty(aura::client::kModalKey) != ui:: MODAL_TYPE_NONE && (*index)->TargetVisibility()) @@ -979,13 +947,13 @@ bool DesktopRootWindowHostWin::IsModalWindowActive() const { } //////////////////////////////////////////////////////////////////////////////// -// DesktopRootWindowHost, public: +// DesktopWindowTreeHost, public: // static -DesktopRootWindowHost* DesktopRootWindowHost::Create( +DesktopWindowTreeHost* DesktopWindowTreeHost::Create( internal::NativeWidgetDelegate* native_widget_delegate, DesktopNativeWidgetAura* desktop_native_widget_aura) { - return new DesktopRootWindowHostWin(native_widget_delegate, + return new DesktopWindowTreeHostWin(native_widget_delegate, desktop_native_widget_aura); } diff --git a/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_win.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h index e4ed8eaeb8f..225365f5b14 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_win.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h @@ -2,19 +2,20 @@ // 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_ +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_WINDOW_TREE_HOST_WIN_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_WINDOW_TREE_HOST_WIN_H_ -#include "ui/aura/client/animation_host.h" #include "ui/aura/window_tree_host.h" #include "ui/views/views_export.h" -#include "ui/views/widget/desktop_aura/desktop_root_window_host.h" +#include "ui/views/widget/desktop_aura/desktop_window_tree_host.h" #include "ui/views/win/hwnd_message_handler_delegate.h" +#include "ui/wm/public/animation_host.h" namespace aura { namespace client { class DragDropClient; class FocusClient; +class ScopedTooltipDisabler; } } @@ -27,33 +28,32 @@ namespace corewm { class TooltipWin; } -class VIEWS_EXPORT DesktopRootWindowHostWin - : public DesktopRootWindowHost, +class VIEWS_EXPORT DesktopWindowTreeHostWin + : public DesktopWindowTreeHost, public aura::client::AnimationHost, - public aura::RootWindowHost, + public aura::WindowTreeHost, + public ui::EventSource, public HWNDMessageHandlerDelegate { public: - DesktopRootWindowHostWin( + DesktopWindowTreeHostWin( internal::NativeWidgetDelegate* native_widget_delegate, DesktopNativeWidgetAura* desktop_native_widget_aura); - virtual ~DesktopRootWindowHostWin(); + virtual ~DesktopWindowTreeHostWin(); // A way of converting an HWND into a content window. static aura::Window* GetContentWindowForHWND(HWND hwnd); protected: - // Overridden from DesktopRootWindowHost: + // Overridden from DesktopWindowTreeHost: virtual void Init(aura::Window* content_window, - const Widget::InitParams& params, - aura::RootWindow::CreateParams* rw_create_params) OVERRIDE; - virtual void OnRootWindowCreated(aura::RootWindow* root, - const Widget::InitParams& params) OVERRIDE; + const Widget::InitParams& params) OVERRIDE; + virtual void OnNativeWidgetCreated(const Widget::InitParams& params) OVERRIDE; virtual scoped_ptr<corewm::Tooltip> CreateTooltip() OVERRIDE; virtual scoped_ptr<aura::client::DragDropClient> CreateDragDropClient(DesktopNativeCursorManager* cursor_manager) OVERRIDE; virtual void Close() OVERRIDE; virtual void CloseNow() OVERRIDE; - virtual aura::RootWindowHost* AsRootWindowHost() OVERRIDE; + virtual aura::WindowTreeHost* AsWindowTreeHost() OVERRIDE; virtual void ShowWindowWithState(ui::WindowShowState show_state) OVERRIDE; virtual void ShowMaximizedWithBounds( const gfx::Rect& restored_bounds) OVERRIDE; @@ -80,7 +80,8 @@ class VIEWS_EXPORT DesktopRootWindowHostWin virtual bool HasCapture() const OVERRIDE; virtual void SetAlwaysOnTop(bool always_on_top) OVERRIDE; virtual bool IsAlwaysOnTop() const OVERRIDE; - virtual bool SetWindowTitle(const string16& title) OVERRIDE; + virtual void SetVisibleOnAllWorkspaces(bool always_visible) OVERRIDE; + virtual bool SetWindowTitle(const base::string16& title) OVERRIDE; virtual void ClearNativeFocus() OVERRIDE; virtual Widget::MoveLoopResult RunMoveLoop( const gfx::Vector2d& drag_offset, @@ -88,9 +89,9 @@ class VIEWS_EXPORT DesktopRootWindowHostWin Widget::MoveLoopEscapeBehavior escape_behavior) OVERRIDE; virtual void EndMoveLoop() OVERRIDE; virtual void SetVisibilityChangedAnimationsEnabled(bool value) OVERRIDE; - virtual bool ShouldUseNativeFrame() OVERRIDE; + virtual bool ShouldUseNativeFrame() const OVERRIDE; + virtual bool ShouldWindowContentsBeTransparent() const 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; @@ -102,29 +103,26 @@ class VIEWS_EXPORT DesktopRootWindowHostWin virtual void OnNativeWidgetFocus() OVERRIDE; virtual void OnNativeWidgetBlur() OVERRIDE; virtual bool IsAnimatingClosed() const OVERRIDE; + virtual bool IsTranslucentWindowOpacitySupported() const OVERRIDE; - // Overridden from aura::RootWindowHost: - virtual aura::RootWindow* GetRootWindow() OVERRIDE; + // Overridden from aura::WindowTreeHost: + virtual ui::EventSource* GetEventSource() 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 PostNativeEvent(const base::NativeEvent& native_event) OVERRIDE; virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE; - virtual void PrepareForShutdown() OVERRIDE; + virtual void SetCursorNative(gfx::NativeCursor cursor) OVERRIDE; + virtual void OnCursorVisibilityChangedNative(bool show) OVERRIDE; + virtual void MoveCursorToNative(const gfx::Point& location) OVERRIDE; + + // Overridden frm ui::EventSource + virtual ui::EventProcessor* GetEventProcessor() OVERRIDE; // Overridden from aura::client::AnimationHost virtual void SetHostTransitionOffsets( @@ -142,10 +140,6 @@ class VIEWS_EXPORT DesktopRootWindowHostWin 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; @@ -171,7 +165,7 @@ class VIEWS_EXPORT DesktopRootWindowHostWin virtual void HandleCreate() OVERRIDE; virtual void HandleDestroying() OVERRIDE; virtual void HandleDestroyed() OVERRIDE; - virtual bool HandleInitialFocus() OVERRIDE; + virtual bool HandleInitialFocus(ui::WindowShowState show_state) OVERRIDE; virtual void HandleDisplayChange() OVERRIDE; virtual void HandleBeginWMSizeMove() OVERRIDE; virtual void HandleEndWMSizeMove() OVERRIDE; @@ -201,6 +195,7 @@ class VIEWS_EXPORT DesktopRootWindowHostWin virtual void HandleTooltipMouseMove(UINT message, WPARAM w_param, LPARAM l_param) OVERRIDE; + virtual void HandleMenuLoop(bool in_menu_loop) OVERRIDE; virtual bool PreHandleMSG(UINT message, WPARAM w_param, LPARAM l_param, @@ -209,6 +204,7 @@ class VIEWS_EXPORT DesktopRootWindowHostWin WPARAM w_param, LPARAM l_param) OVERRIDE; virtual bool HandleScrollEvent(const ui::ScrollEvent& event) OVERRIDE; + virtual void HandleWindowSizeChanging() OVERRIDE; Widget* GetWidget(); const Widget* GetWidget() const; @@ -220,9 +216,6 @@ class VIEWS_EXPORT DesktopRootWindowHostWin // Returns true if a modal window is active in the current root window chain. bool IsModalWindowActive() const; - // 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<aura::client::FocusClient> focus_client_; @@ -267,12 +260,17 @@ class VIEWS_EXPORT DesktopRootWindowHostWin // a reference. corewm::TooltipWin* tooltip_; - // State of the cursor. - bool is_cursor_visible_; + // Visibility of the cursor. On Windows we can have multiple root windows and + // the implementation of ::ShowCursor() is based on a counter, so making this + // member static ensures that ::ShowCursor() is always called exactly once + // whenever the cursor visibility state changes. + static bool is_cursor_visible_; + + scoped_ptr<aura::client::ScopedTooltipDisabler> tooltip_disabler_; - DISALLOW_COPY_AND_ASSIGN(DesktopRootWindowHostWin); + DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostWin); }; } // namespace views -#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_ROOT_WINDOW_HOST_WIN_H_ +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_WINDOW_TREE_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_window_tree_host_x11.cc index 545e0cde550..9a96e53c283 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_x11.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc @@ -2,7 +2,7 @@ // 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 "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" #include <X11/extensions/shape.h> #include <X11/extensions/XInput2.h> @@ -11,20 +11,22 @@ #include <X11/Xutil.h> #include "base/basictypes.h" +#include "base/command_line.h" #include "base/debug/trace_event.h" -#include "base/message_loop/message_pump_x11.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/cursor_client.h" #include "ui/aura/client/focus_client.h" -#include "ui/aura/client/user_action_client.h" -#include "ui/aura/root_window.h" #include "ui/aura/window.h" +#include "ui/aura/window_event_dispatcher.h" #include "ui/aura/window_property.h" #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h" +#include "ui/base/hit_test.h" #include "ui/base/x/x11_util.h" #include "ui/events/event_utils.h" +#include "ui/events/platform/platform_event_source.h" +#include "ui/events/platform/x11/x11_event_source.h" #include "ui/events/x/device_data_manager.h" #include "ui/events/x/device_list_cache_x.h" #include "ui/events/x/touch_factory_x11.h" @@ -33,49 +35,54 @@ #include "ui/gfx/insets.h" #include "ui/gfx/path.h" #include "ui/gfx/path_x11.h" +#include "ui/gfx/screen.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/tooltip_aura.h" #include "ui/views/ime/input_method.h" #include "ui/views/linux_ui/linux_ui.h" #include "ui/views/views_delegate.h" +#include "ui/views/views_switches.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_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_window_tree_host_observer_x11.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_scoped_capture.h" #include "ui/views/widget/desktop_aura/x11_window_event_filter.h" +#include "ui/wm/core/compound_event_filter.h" +#include "ui/wm/core/window_util.h" namespace views { -DesktopRootWindowHostX11* DesktopRootWindowHostX11::g_current_capture = +DesktopWindowTreeHostX11* DesktopWindowTreeHostX11::g_current_capture = NULL; -std::list<XID>* DesktopRootWindowHostX11::open_windows_ = NULL; +std::list<XID>* DesktopWindowTreeHostX11::open_windows_ = NULL; DEFINE_WINDOW_PROPERTY_KEY( aura::Window*, kViewsWindowForRootWindow, NULL); DEFINE_WINDOW_PROPERTY_KEY( - DesktopRootWindowHostX11*, kHostForRootWindow, NULL); + DesktopWindowTreeHostX11*, 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; +// Special value of the _NET_WM_DESKTOP property which indicates that the window +// should appear on all desktops. +const int kAllDesktops = 0xFFFFFFFF; + const char* kAtomsToCache[] = { "UTF8_STRING", "WM_DELETE_WINDOW", "WM_PROTOCOLS", - "WM_S0", + "_NET_FRAME_EXTENTS", + "_NET_WM_CM_S0", + "_NET_WM_DESKTOP", "_NET_WM_ICON", "_NET_WM_NAME", "_NET_WM_PID", @@ -87,6 +94,8 @@ const char* kAtomsToCache[] = { "_NET_WM_STATE_MAXIMIZED_HORZ", "_NET_WM_STATE_MAXIMIZED_VERT", "_NET_WM_STATE_SKIP_TASKBAR", + "_NET_WM_STATE_STICKY", + "_NET_WM_USER_TIME", "_NET_WM_WINDOW_OPACITY", "_NET_WM_WINDOW_TYPE", "_NET_WM_WINDOW_TYPE_DND", @@ -116,9 +125,9 @@ const char* kAtomsToCache[] = { } // namespace //////////////////////////////////////////////////////////////////////////////// -// DesktopRootWindowHostX11, public: +// DesktopWindowTreeHostX11, public: -DesktopRootWindowHostX11::DesktopRootWindowHostX11( +DesktopWindowTreeHostX11::DesktopWindowTreeHostX11( internal::NativeWidgetDelegate* native_widget_delegate, DesktopNativeWidgetAura* desktop_native_widget_aura) : close_widget_factory_(this), @@ -129,38 +138,44 @@ DesktopRootWindowHostX11::DesktopRootWindowHostX11( window_mapped_(false), is_fullscreen_(false), is_always_on_top_(false), - root_window_(NULL), + use_native_frame_(false), + use_argb_visual_(false), drag_drop_client_(NULL), current_cursor_(ui::kCursorNull), native_widget_delegate_(native_widget_delegate), desktop_native_widget_aura_(desktop_native_widget_aura), content_window_(NULL), window_parent_(NULL), - custom_window_shape_(NULL) { + window_shape_(NULL), + custom_window_shape_(false), + urgency_hint_set_(false) { } -DesktopRootWindowHostX11::~DesktopRootWindowHostX11() { - root_window_->window()->ClearProperty(kHostForRootWindow); - aura::client::SetWindowMoveClient(root_window_->window(), NULL); - desktop_native_widget_aura_->OnDesktopRootWindowHostDestroyed(root_window_); - if (custom_window_shape_) - XDestroyRegion(custom_window_shape_); +DesktopWindowTreeHostX11::~DesktopWindowTreeHostX11() { + window()->ClearProperty(kHostForRootWindow); + aura::client::SetWindowMoveClient(window(), NULL); + desktop_native_widget_aura_->OnDesktopWindowTreeHostDestroyed(this); + if (window_shape_) + XDestroyRegion(window_shape_); + DestroyDispatcher(); } // static -aura::Window* DesktopRootWindowHostX11::GetContentWindowForXID(XID xid) { - aura::RootWindow* root = aura::RootWindow::GetForAcceleratedWidget(xid); - return root ? root->window()->GetProperty(kViewsWindowForRootWindow) : NULL; +aura::Window* DesktopWindowTreeHostX11::GetContentWindowForXID(XID xid) { + aura::WindowTreeHost* host = + aura::WindowTreeHost::GetForAcceleratedWidget(xid); + return host ? host->window()->GetProperty(kViewsWindowForRootWindow) : NULL; } // static -DesktopRootWindowHostX11* DesktopRootWindowHostX11::GetHostForXID(XID xid) { - aura::RootWindow* root = aura::RootWindow::GetForAcceleratedWidget(xid); - return root ? root->window()->GetProperty(kHostForRootWindow) : NULL; +DesktopWindowTreeHostX11* DesktopWindowTreeHostX11::GetHostForXID(XID xid) { + aura::WindowTreeHost* host = + aura::WindowTreeHost::GetForAcceleratedWidget(xid); + return host ? host->window()->GetProperty(kHostForRootWindow) : NULL; } // static -std::vector<aura::Window*> DesktopRootWindowHostX11::GetAllOpenWindows() { +std::vector<aura::Window*> DesktopWindowTreeHostX11::GetAllOpenWindows() { std::vector<aura::Window*> windows(open_windows().size()); std::transform(open_windows().begin(), open_windows().end(), @@ -169,14 +184,25 @@ std::vector<aura::Window*> DesktopRootWindowHostX11::GetAllOpenWindows() { return windows; } -gfx::Rect DesktopRootWindowHostX11::GetX11RootWindowBounds() const { +gfx::Rect DesktopWindowTreeHostX11::GetX11RootWindowBounds() const { return bounds_; } -void DesktopRootWindowHostX11::HandleNativeWidgetActivationChanged( +gfx::Rect DesktopWindowTreeHostX11::GetX11RootWindowOuterBounds() const { + gfx::Rect outer_bounds(bounds_); + outer_bounds.Inset(-native_window_frame_borders_); + return outer_bounds; +} + +::Region DesktopWindowTreeHostX11::GetWindowShape() const { + return window_shape_; +} + +void DesktopWindowTreeHostX11::HandleNativeWidgetActivationChanged( bool active) { if (active) { - delegate_->OnHostActivated(); + FlashFrame(false); + OnHostActivated(); open_windows().remove(xwindow_); open_windows().insert(open_windows().begin(), xwindow_); } @@ -186,31 +212,39 @@ void DesktopRootWindowHostX11::HandleNativeWidgetActivationChanged( native_widget_delegate_->AsWidget()->GetRootView()->SchedulePaint(); } -void DesktopRootWindowHostX11::AddObserver( - views::DesktopRootWindowHostObserverX11* observer) { +void DesktopWindowTreeHostX11::AddObserver( + views::DesktopWindowTreeHostObserverX11* observer) { observer_list_.AddObserver(observer); } -void DesktopRootWindowHostX11::RemoveObserver( - views::DesktopRootWindowHostObserverX11* observer) { +void DesktopWindowTreeHostX11::RemoveObserver( + views::DesktopWindowTreeHostObserverX11* observer) { observer_list_.RemoveObserver(observer); } -void DesktopRootWindowHostX11::CleanUpWindowList() { +void DesktopWindowTreeHostX11::SwapNonClientEventHandler( + scoped_ptr<ui::EventHandler> handler) { + wm::CompoundEventFilter* compound_event_filter = + desktop_native_widget_aura_->root_window_event_filter(); + if (x11_non_client_event_filter_) + compound_event_filter->RemoveHandler(x11_non_client_event_filter_.get()); + compound_event_filter->AddHandler(handler.get()); + x11_non_client_event_filter_ = handler.Pass(); +} + +void DesktopWindowTreeHostX11::CleanUpWindowList() { delete open_windows_; open_windows_ = NULL; } //////////////////////////////////////////////////////////////////////////////// -// DesktopRootWindowHostX11, DesktopRootWindowHost implementation: +// DesktopWindowTreeHostX11, DesktopWindowTreeHost implementation: -void DesktopRootWindowHostX11::Init( - aura::Window* content_window, - const Widget::InitParams& params, - aura::RootWindow::CreateParams* rw_create_params) { +void DesktopWindowTreeHostX11::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 + // TODO(erg): Check whether we *should* be building a WindowTreeHost here, or // whether we should be proxying requests to another DRWHL. // In some situations, views tries to make a zero sized window, and that @@ -222,59 +256,47 @@ void DesktopRootWindowHostX11::Init( sanitized_params.bounds.set_height(100); InitX11Window(sanitized_params); - - rw_create_params->initial_bounds = bounds_; - rw_create_params->host = this; } -void DesktopRootWindowHostX11::OnRootWindowCreated( - aura::RootWindow* root, +void DesktopWindowTreeHostX11::OnNativeWidgetCreated( const Widget::InitParams& params) { - root_window_ = root; - - root_window_->window()->SetProperty(kViewsWindowForRootWindow, - content_window_); - root_window_->window()->SetProperty(kHostForRootWindow, this); - 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_); + window()->SetProperty(kViewsWindowForRootWindow, content_window_); + window()->SetProperty(kHostForRootWindow, this); // Ensure that the X11DesktopHandler exists so that it dispatches activation // messages to us. X11DesktopHandler::get(); // TODO(erg): Unify this code once the other consumer goes away. - x11_window_event_filter_.reset(new X11WindowEventFilter(root_window_, this)); - x11_window_event_filter_->SetUseHostWindowBorders(false); - desktop_native_widget_aura_->root_window_event_filter()->AddHandler( - x11_window_event_filter_.get()); + SwapNonClientEventHandler( + scoped_ptr<ui::EventHandler>(new X11WindowEventFilter(this)).Pass()); + SetUseNativeFrame(params.type == Widget::InitParams::TYPE_WINDOW && + !params.remove_standard_frame); x11_window_move_client_.reset(new X11DesktopWindowMoveClient); - aura::client::SetWindowMoveClient(root_window_->window(), - x11_window_move_client_.get()); + aura::client::SetWindowMoveClient(window(), x11_window_move_client_.get()); + + SetWindowTransparency(); native_widget_delegate_->OnNativeWidgetCreated(true); } -scoped_ptr<corewm::Tooltip> DesktopRootWindowHostX11::CreateTooltip() { +scoped_ptr<corewm::Tooltip> DesktopWindowTreeHostX11::CreateTooltip() { return scoped_ptr<corewm::Tooltip>( new corewm::TooltipAura(gfx::SCREEN_TYPE_NATIVE)); } scoped_ptr<aura::client::DragDropClient> -DesktopRootWindowHostX11::CreateDragDropClient( +DesktopWindowTreeHostX11::CreateDragDropClient( DesktopNativeCursorManager* cursor_manager) { drag_drop_client_ = new DesktopDragDropClientAuraX11( - root_window_->window(), cursor_manager, xdisplay_, xwindow_); + window(), cursor_manager, xdisplay_, xwindow_); return scoped_ptr<aura::client::DragDropClient>(drag_drop_client_).Pass(); } -void DesktopRootWindowHostX11::Close() { +void DesktopWindowTreeHostX11::Close() { // TODO(erg): Might need to do additional hiding tasks here. + delayed_resize_task_.Cancel(); if (!close_widget_factory_.HasWeakPtrs()) { // And we delay the close so that if we are called from an ATL callback, @@ -283,21 +305,22 @@ void DesktopRootWindowHostX11::Close() { // dereference us when the callback returns). base::MessageLoop::current()->PostTask( FROM_HERE, - base::Bind(&DesktopRootWindowHostX11::CloseNow, + base::Bind(&DesktopWindowTreeHostX11::CloseNow, close_widget_factory_.GetWeakPtr())); } } -void DesktopRootWindowHostX11::CloseNow() { +void DesktopWindowTreeHostX11::CloseNow() { if (xwindow_ == None) return; + x11_capture_.reset(); native_widget_delegate_->OnNativeWidgetDestroying(); // If we have children, close them. Use a copy for iteration because they'll // remove themselves. - std::set<DesktopRootWindowHostX11*> window_children_copy = window_children_; - for (std::set<DesktopRootWindowHostX11*>::iterator it = + std::set<DesktopWindowTreeHostX11*> window_children_copy = window_children_; + for (std::set<DesktopWindowTreeHostX11*>::iterator it = window_children_copy.begin(); it != window_children_copy.end(); ++it) { (*it)->CloseNow(); @@ -311,62 +334,79 @@ void DesktopRootWindowHostX11::CloseNow() { } // Remove the event listeners we've installed. We need to remove these - // because otherwise we get assert during ~RootWindow(). + // because otherwise we get assert during ~WindowEventDispatcher(). desktop_native_widget_aura_->root_window_event_filter()->RemoveHandler( - x11_window_event_filter_.get()); + x11_non_client_event_filter_.get()); + x11_non_client_event_filter_.reset(); + + // Destroy the compositor before destroying the |xwindow_| since shutdown + // may try to swap, and the swap without a window causes an X error, which + // causes a crash with in-process renderer. + DestroyCompositor(); open_windows().remove(xwindow_); // Actually free our native resources. - base::MessagePumpX11::Current()->RemoveDispatcherForWindow(xwindow_); + if (ui::PlatformEventSource::GetInstance()) + ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); XDestroyWindow(xdisplay_, xwindow_); xwindow_ = None; desktop_native_widget_aura_->OnHostClosed(); } -aura::RootWindowHost* DesktopRootWindowHostX11::AsRootWindowHost() { +aura::WindowTreeHost* DesktopWindowTreeHostX11::AsWindowTreeHost() { return this; } -void DesktopRootWindowHostX11::ShowWindowWithState( +void DesktopWindowTreeHostX11::ShowWindowWithState( ui::WindowShowState show_state) { - if (show_state != ui::SHOW_STATE_DEFAULT && - show_state != ui::SHOW_STATE_NORMAL) { - // Only forwarding to Show(). - NOTIMPLEMENTED(); + if (!window_mapped_) + MapWindow(show_state); + + if (show_state == ui::SHOW_STATE_NORMAL || + show_state == ui::SHOW_STATE_MAXIMIZED) { + // Note: XFCE ignores a maximize hint given before mapping the window. + if (show_state == ui::SHOW_STATE_MAXIMIZED) + Maximize(); + Activate(); } - Show(); + native_widget_delegate_->AsWidget()->SetInitialFocus(show_state); } -void DesktopRootWindowHostX11::ShowMaximizedWithBounds( +void DesktopWindowTreeHostX11::ShowMaximizedWithBounds( const gfx::Rect& restored_bounds) { + ShowWindowWithState(ui::SHOW_STATE_MAXIMIZED); + // Enforce |restored_bounds_| since calling Maximize() could have reset it. restored_bounds_ = restored_bounds; - Maximize(); - Show(); } -bool DesktopRootWindowHostX11::IsVisible() const { +bool DesktopWindowTreeHostX11::IsVisible() const { return window_mapped_; } -void DesktopRootWindowHostX11::SetSize(const gfx::Size& size) { - // TODO(erg): - NOTIMPLEMENTED(); +void DesktopWindowTreeHostX11::SetSize(const gfx::Size& size) { + bool size_changed = bounds_.size() != size; + XResizeWindow(xdisplay_, xwindow_, size.width(), size.height()); + bounds_.set_size(size); + if (size_changed) { + OnHostResized(size); + ResetWindowRegion(); + } } -void DesktopRootWindowHostX11::StackAtTop() { +void DesktopWindowTreeHostX11::StackAtTop() { XRaiseWindow(xdisplay_, xwindow_); } -void DesktopRootWindowHostX11::CenterWindow(const gfx::Size& size) { +void DesktopWindowTreeHostX11::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()) { + if (wm::GetTransientParent(content_window_)) { gfx::Rect transient_parent_rect = - content_window_->transient_parent()->GetBoundsInScreen(); + wm::GetTransientParent(content_window_)->GetBoundsInScreen(); if (transient_parent_rect.height() >= size.height() && transient_parent_rect.width() >= size.width()) { parent_bounds = transient_parent_rect; @@ -385,10 +425,10 @@ void DesktopRootWindowHostX11::CenterWindow(const gfx::Size& size) { SetBounds(window_bounds); } -void DesktopRootWindowHostX11::GetWindowPlacement( +void DesktopWindowTreeHostX11::GetWindowPlacement( gfx::Rect* bounds, ui::WindowShowState* show_state) const { - *bounds = bounds_; + *bounds = GetRestoredBounds(); if (IsFullscreen()) { *show_state = ui::SHOW_STATE_FULLSCREEN; @@ -403,14 +443,14 @@ void DesktopRootWindowHostX11::GetWindowPlacement( } } -gfx::Rect DesktopRootWindowHostX11::GetWindowBoundsInScreen() const { +gfx::Rect DesktopWindowTreeHostX11::GetWindowBoundsInScreen() const { return bounds_; } -gfx::Rect DesktopRootWindowHostX11::GetClientAreaBoundsInScreen() const { +gfx::Rect DesktopWindowTreeHostX11::GetClientAreaBoundsInScreen() const { // TODO(erg): The NativeWidgetAura version returns |bounds_|, claiming its // needed for View::ConvertPointToScreen() to work - // correctly. DesktopRootWindowHostWin::GetClientAreaBoundsInScreen() just + // correctly. DesktopWindowTreeHostWin::GetClientAreaBoundsInScreen() just // asks windows what it thinks the client rect is. // // Attempts to calculate the rect by asking the NonClientFrameView what it @@ -419,7 +459,7 @@ gfx::Rect DesktopRootWindowHostX11::GetClientAreaBoundsInScreen() const { return bounds_; } -gfx::Rect DesktopRootWindowHostX11::GetRestoredBounds() const { +gfx::Rect DesktopWindowTreeHostX11::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 @@ -430,7 +470,7 @@ gfx::Rect DesktopRootWindowHostX11::GetRestoredBounds() const { return GetWindowBoundsInScreen(); } -gfx::Rect DesktopRootWindowHostX11::GetWorkAreaBoundsInScreen() const { +gfx::Rect DesktopWindowTreeHostX11::GetWorkAreaBoundsInScreen() const { std::vector<int> value; if (ui::GetIntArrayProperty(x_root_window_, "_NET_WORKAREA", &value) && value.size() >= 4) { @@ -451,79 +491,118 @@ gfx::Rect DesktopRootWindowHostX11::GetWorkAreaBoundsInScreen() const { return gfx::Rect(x, y, width, height); } -void DesktopRootWindowHostX11::SetShape(gfx::NativeRegion native_region) { - if (custom_window_shape_) - XDestroyRegion(custom_window_shape_); - custom_window_shape_ = gfx::CreateRegionFromSkRegion(*native_region); +void DesktopWindowTreeHostX11::SetShape(gfx::NativeRegion native_region) { + if (window_shape_) + XDestroyRegion(window_shape_); + custom_window_shape_ = true; + window_shape_ = gfx::CreateRegionFromSkRegion(*native_region); ResetWindowRegion(); delete native_region; } -void DesktopRootWindowHostX11::Activate() { +void DesktopWindowTreeHostX11::Activate() { + if (!window_mapped_) + return; + X11DesktopHandler::get()->ActivateWindow(xwindow_); - native_widget_delegate_->AsWidget()->SetInitialFocus(); } -void DesktopRootWindowHostX11::Deactivate() { - // Deactivating a window means activating nothing. - X11DesktopHandler::get()->ActivateWindow(None); +void DesktopWindowTreeHostX11::Deactivate() { + if (!IsActive()) + return; + + x11_capture_.reset(); + XLowerWindow(xdisplay_, xwindow_); } -bool DesktopRootWindowHostX11::IsActive() const { +bool DesktopWindowTreeHostX11::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. +void DesktopWindowTreeHostX11::Maximize() { + // When we are in the process of requesting to maximize a window, 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")); + if (IsMinimized()) + ShowWindowWithState(ui::SHOW_STATE_NORMAL); } -void DesktopRootWindowHostX11::Minimize() { +void DesktopWindowTreeHostX11::Minimize() { + x11_capture_.reset(); XIconifyWindow(xdisplay_, xwindow_, 0); } -void DesktopRootWindowHostX11::Restore() { +void DesktopWindowTreeHostX11::Restore() { SetWMSpecState(false, atom_cache_.GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"), atom_cache_.GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ")); + if (IsMinimized()) + ShowWindowWithState(ui::SHOW_STATE_NORMAL); } -bool DesktopRootWindowHostX11::IsMaximized() const { +bool DesktopWindowTreeHostX11::IsMaximized() const { return (HasWMSpecProperty("_NET_WM_STATE_MAXIMIZED_VERT") && HasWMSpecProperty("_NET_WM_STATE_MAXIMIZED_HORZ")); } -bool DesktopRootWindowHostX11::IsMinimized() const { +bool DesktopWindowTreeHostX11::IsMinimized() const { return HasWMSpecProperty("_NET_WM_STATE_HIDDEN"); } - -bool DesktopRootWindowHostX11::HasCapture() const { +bool DesktopWindowTreeHostX11::HasCapture() const { return g_current_capture == this; } -void DesktopRootWindowHostX11::SetAlwaysOnTop(bool always_on_top) { +void DesktopWindowTreeHostX11::SetAlwaysOnTop(bool always_on_top) { is_always_on_top_ = always_on_top; SetWMSpecState(always_on_top, atom_cache_.GetAtom("_NET_WM_STATE_ABOVE"), None); } -bool DesktopRootWindowHostX11::IsAlwaysOnTop() const { +bool DesktopWindowTreeHostX11::IsAlwaysOnTop() const { return is_always_on_top_; } -bool DesktopRootWindowHostX11::SetWindowTitle(const string16& title) { +void DesktopWindowTreeHostX11::SetVisibleOnAllWorkspaces(bool always_visible) { + SetWMSpecState(always_visible, + atom_cache_.GetAtom("_NET_WM_STATE_STICKY"), + None); + + int new_desktop = 0; + if (always_visible) { + new_desktop = kAllDesktops; + } else { + if (!ui::GetCurrentDesktop(&new_desktop)) + return; + } + + XEvent xevent; + memset (&xevent, 0, sizeof (xevent)); + xevent.type = ClientMessage; + xevent.xclient.window = xwindow_; + xevent.xclient.message_type = atom_cache_.GetAtom("_NET_WM_DESKTOP"); + xevent.xclient.format = 32; + xevent.xclient.data.l[0] = new_desktop; + xevent.xclient.data.l[1] = 0; + xevent.xclient.data.l[2] = 0; + xevent.xclient.data.l[3] = 0; + xevent.xclient.data.l[4] = 0; + XSendEvent(xdisplay_, x_root_window_, False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xevent); +} + +bool DesktopWindowTreeHostX11::SetWindowTitle(const base::string16& title) { if (window_title_ == title) return false; window_title_ = title; - std::string utf8str = UTF16ToUTF8(title); + std::string utf8str = base::UTF16ToUTF8(title); XChangeProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("_NET_WM_NAME"), @@ -540,7 +619,7 @@ bool DesktopRootWindowHostX11::SetWindowTitle(const string16& title) { return true; } -void DesktopRootWindowHostX11::ClearNativeFocus() { +void DesktopWindowTreeHostX11::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. @@ -551,7 +630,7 @@ void DesktopRootWindowHostX11::ClearNativeFocus() { } } -Widget::MoveLoopResult DesktopRootWindowHostX11::RunMoveLoop( +Widget::MoveLoopResult DesktopWindowTreeHostX11::RunMoveLoop( const gfx::Vector2d& drag_offset, Widget::MoveLoopSource source, Widget::MoveLoopEscapeBehavior escape_behavior) { @@ -566,42 +645,75 @@ Widget::MoveLoopResult DesktopRootWindowHostX11::RunMoveLoop( return Widget::MOVE_LOOP_CANCELED; } -void DesktopRootWindowHostX11::EndMoveLoop() { +void DesktopWindowTreeHostX11::EndMoveLoop() { x11_window_move_client_->EndMoveLoop(); } -void DesktopRootWindowHostX11::SetVisibilityChangedAnimationsEnabled( +void DesktopWindowTreeHostX11::SetVisibilityChangedAnimationsEnabled( bool value) { // Much like the previous NativeWidgetGtk, we don't have anything to do here. } -bool DesktopRootWindowHostX11::ShouldUseNativeFrame() { +bool DesktopWindowTreeHostX11::ShouldUseNativeFrame() const { + return use_native_frame_; +} + +bool DesktopWindowTreeHostX11::ShouldWindowContentsBeTransparent() const { return false; } -void DesktopRootWindowHostX11::FrameTypeChanged() { +void DesktopWindowTreeHostX11::FrameTypeChanged() { + Widget::FrameType new_type = + native_widget_delegate_->AsWidget()->frame_type(); + if (new_type == Widget::FRAME_TYPE_DEFAULT) { + // The default is determined by Widget::InitParams::remove_standard_frame + // and does not change. + return; + } + + SetUseNativeFrame(new_type == Widget::FRAME_TYPE_FORCE_NATIVE); // 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(); } -NonClientFrameView* DesktopRootWindowHostX11::CreateNonClientFrameView() { - return NULL; -} - -void DesktopRootWindowHostX11::SetFullscreen(bool fullscreen) { +void DesktopWindowTreeHostX11::SetFullscreen(bool fullscreen) { + if (is_fullscreen_ == fullscreen) + return; is_fullscreen_ = fullscreen; SetWMSpecState(fullscreen, atom_cache_.GetAtom("_NET_WM_STATE_FULLSCREEN"), None); + // Try to guess the size we will have after the switch to/from fullscreen: + // - (may) avoid transient states + // - works around Flash content which expects to have the size updated + // synchronously. + // See https://crbug.com/361408 + if (fullscreen) { + restored_bounds_ = bounds_; + const gfx::Display display = + gfx::Screen::GetScreenFor(NULL)->GetDisplayNearestWindow(window()); + bounds_ = display.bounds(); + } else { + bounds_ = restored_bounds_; + } + OnHostMoved(bounds_.origin()); + OnHostResized(bounds_.size()); + + if (HasWMSpecProperty("_NET_WM_STATE_FULLSCREEN") == fullscreen) { + Relayout(); + ResetWindowRegion(); + } + // Else: the widget will be relaid out either when the window bounds change or + // when |xwindow_|'s fullscreen state changes. } -bool DesktopRootWindowHostX11::IsFullscreen() const { +bool DesktopWindowTreeHostX11::IsFullscreen() const { return is_fullscreen_; } -void DesktopRootWindowHostX11::SetOpacity(unsigned char opacity) { +void DesktopWindowTreeHostX11::SetOpacity(unsigned char opacity) { // X server opacity is in terms of 32 bit unsigned int space, and counts from // the opposite direction. // XChangeProperty() expects "cardinality" to be long. @@ -619,7 +731,7 @@ void DesktopRootWindowHostX11::SetOpacity(unsigned char opacity) { } } -void DesktopRootWindowHostX11::SetWindowIcons( +void DesktopWindowTreeHostX11::SetWindowIcons( const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon) { // TODO(erg): The way we handle icons across different versions of chrome // could be substantially improved. The Windows version does its own thing @@ -643,7 +755,7 @@ void DesktopRootWindowHostX11::SetWindowIcons( ui::SetAtomArrayProperty(xwindow_, "_NET_WM_ICON", "CARDINAL", data); } -void DesktopRootWindowHostX11::InitModalType(ui::ModalType modal_type) { +void DesktopWindowTreeHostX11::InitModalType(ui::ModalType modal_type) { switch (modal_type) { case ui::MODAL_TYPE_NONE: break; @@ -655,12 +767,28 @@ void DesktopRootWindowHostX11::InitModalType(ui::ModalType modal_type) { } } -void DesktopRootWindowHostX11::FlashFrame(bool flash_frame) { - // TODO(erg): - NOTIMPLEMENTED(); +void DesktopWindowTreeHostX11::FlashFrame(bool flash_frame) { + if (urgency_hint_set_ == flash_frame) + return; + + XWMHints* hints = XGetWMHints(xdisplay_, xwindow_); + if (!hints) { + // The window hasn't had its hints set yet. + hints = XAllocWMHints(); + } + + if (flash_frame) + hints->flags |= XUrgencyHint; + else + hints->flags &= ~XUrgencyHint; + + XSetWMHints(xdisplay_, xwindow_, hints); + XFree(hints); + + urgency_hint_set_ = flash_frame; } -void DesktopRootWindowHostX11::OnRootViewLayout() const { +void DesktopWindowTreeHostX11::OnRootViewLayout() const { if (!window_mapped_) return; @@ -689,68 +817,54 @@ void DesktopRootWindowHostX11::OnRootViewLayout() const { XSetWMNormalHints(xdisplay_, xwindow_, &hints); } -void DesktopRootWindowHostX11::OnNativeWidgetFocus() { +void DesktopWindowTreeHostX11::OnNativeWidgetFocus() { native_widget_delegate_->AsWidget()->GetInputMethod()->OnFocus(); } -void DesktopRootWindowHostX11::OnNativeWidgetBlur() { - if (xwindow_) +void DesktopWindowTreeHostX11::OnNativeWidgetBlur() { + if (xwindow_) { + x11_capture_.reset(); native_widget_delegate_->AsWidget()->GetInputMethod()->OnBlur(); + } } -bool DesktopRootWindowHostX11::IsAnimatingClosed() const { +bool DesktopWindowTreeHostX11::IsAnimatingClosed() const { + return false; +} + +bool DesktopWindowTreeHostX11::IsTranslucentWindowOpacitySupported() const { return false; } //////////////////////////////////////////////////////////////////////////////// -// DesktopRootWindowHostX11, aura::RootWindowHost implementation: +// DesktopWindowTreeHostX11, aura::WindowTreeHost implementation: -aura::RootWindow* DesktopRootWindowHostX11::GetRootWindow() { - return root_window_; +ui::EventSource* DesktopWindowTreeHostX11::GetEventSource() { + return this; } -gfx::AcceleratedWidget DesktopRootWindowHostX11::GetAcceleratedWidget() { +gfx::AcceleratedWidget DesktopWindowTreeHostX11::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::MessagePumpX11::Current()->BlockUntilWindowMapped(xwindow_); - window_mapped_ = true; - } - - native_widget_delegate_->AsWidget()->SetInitialFocus(); +void DesktopWindowTreeHostX11::Show() { + ShowWindowWithState(ui::SHOW_STATE_NORMAL); + native_widget_delegate_->OnNativeWidgetVisibilityChanged(true); } -void DesktopRootWindowHostX11::Hide() { +void DesktopWindowTreeHostX11::Hide() { if (window_mapped_) { XWithdrawWindow(xdisplay_, xwindow_, 0); window_mapped_ = false; } + native_widget_delegate_->OnNativeWidgetVisibilityChanged(false); } -void DesktopRootWindowHostX11::ToggleFullScreen() { - NOTIMPLEMENTED(); -} - -gfx::Rect DesktopRootWindowHostX11::GetBounds() const { +gfx::Rect DesktopWindowTreeHostX11::GetBounds() const { return bounds_; } -void DesktopRootWindowHostX11::SetBounds(const gfx::Rect& bounds) { +void DesktopWindowTreeHostX11::SetBounds(const gfx::Rect& bounds) { bool origin_changed = bounds_.origin() != bounds.origin(); bool size_changed = bounds_.size() != bounds.size(); XWindowChanges changes = {0}; @@ -783,24 +897,17 @@ void DesktopRootWindowHostX11::SetBounds(const gfx::Rect& bounds) { if (origin_changed) native_widget_delegate_->AsWidget()->OnNativeWidgetMove(); - if (size_changed) - delegate_->OnHostResized(bounds.size()); - else - delegate_->OnHostPaint(gfx::Rect(bounds.size())); -} - -gfx::Insets DesktopRootWindowHostX11::GetInsets() const { - return gfx::Insets(); -} - -void DesktopRootWindowHostX11::SetInsets(const gfx::Insets& insets) { + if (size_changed) { + OnHostResized(bounds.size()); + ResetWindowRegion(); + } } -gfx::Point DesktopRootWindowHostX11::GetLocationOnNativeScreen() const { +gfx::Point DesktopWindowTreeHostX11::GetLocationOnNativeScreen() const { return bounds_.origin(); } -void DesktopRootWindowHostX11::SetCapture() { +void DesktopWindowTreeHostX11::SetCapture() { // This is vaguely based on the old NativeWidgetGtk implementation. // // X11's XPointerGrab() shouldn't be used for everything; it doesn't map @@ -813,70 +920,29 @@ void DesktopRootWindowHostX11::SetCapture() { 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. + x11_capture_.reset(new X11ScopedCapture(xwindow_)); } -void DesktopRootWindowHostX11::ReleaseCapture() { - if (g_current_capture) +void DesktopWindowTreeHostX11::ReleaseCapture() { + if (g_current_capture == this) g_current_capture->OnCaptureReleased(); } -void DesktopRootWindowHostX11::SetCursor(gfx::NativeCursor cursor) { +void DesktopWindowTreeHostX11::SetCursorNative(gfx::NativeCursor cursor) { XDefineCursor(xdisplay_, xwindow_, cursor.platform()); } -bool DesktopRootWindowHostX11::QueryMouseLocation( - gfx::Point* location_return) { - aura::client::CursorClient* cursor_client = - aura::client::GetCursorClient(GetRootWindow()->window()); - 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 DesktopWindowTreeHostX11::MoveCursorToNative(const gfx::Point& location) { + XWarpPointer(xdisplay_, None, x_root_window_, 0, 0, 0, 0, + bounds_.x() + location.x(), bounds_.y() + location.y()); } -void DesktopRootWindowHostX11::OnCursorVisibilityChanged(bool show) { +void DesktopWindowTreeHostX11::OnCursorVisibilityChangedNative(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::PostNativeEvent( +void DesktopWindowTreeHostX11::PostNativeEvent( const base::NativeEvent& native_event) { DCHECK(xwindow_); DCHECK(xdisplay_); @@ -909,17 +975,21 @@ void DesktopRootWindowHostX11::PostNativeEvent( XSendEvent(xdisplay_, xwindow_, False, 0, &xevent); } -void DesktopRootWindowHostX11::OnDeviceScaleFactorChanged( +void DesktopWindowTreeHostX11::OnDeviceScaleFactorChanged( float device_scale_factor) { } -void DesktopRootWindowHostX11::PrepareForShutdown() { +//////////////////////////////////////////////////////////////////////////////// +// DesktopWindowTreeHostX11, ui::EventSource implementation: + +ui::EventProcessor* DesktopWindowTreeHostX11::GetEventProcessor() { + return dispatcher(); } //////////////////////////////////////////////////////////////////////////////// -// DesktopRootWindowHostX11, private: +// DesktopWindowTreeHostX11, private: -void DesktopRootWindowHostX11::InitX11Window( +void DesktopWindowTreeHostX11::InitX11Window( const Widget::InitParams& params) { unsigned long attribute_mask = CWBackPixmap; XSetWindowAttributes swa; @@ -933,6 +1003,7 @@ void DesktopRootWindowHostX11::InitX11Window( window_type = atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_MENU"); break; case Widget::InitParams::TYPE_TOOLTIP: + swa.override_redirect = True; window_type = atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_TOOLTIP"); break; case Widget::InitParams::TYPE_POPUP: @@ -950,18 +1021,45 @@ void DesktopRootWindowHostX11::InitX11Window( if (swa.override_redirect) attribute_mask |= CWOverrideRedirect; + // Detect whether we're running inside a compositing manager. If so, try to + // use the ARGB visual. Otherwise, just use our parent's visual. + Visual* visual = CopyFromParent; + int depth = CopyFromParent; + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableTransparentVisuals) && + XGetSelectionOwner(xdisplay_, + atom_cache_.GetAtom("_NET_WM_CM_S0")) != None) { + Visual* rgba_visual = GetARGBVisual(); + if (rgba_visual) { + visual = rgba_visual; + depth = 32; + + attribute_mask |= CWColormap; + swa.colormap = XCreateColormap(xdisplay_, x_root_window_, visual, + AllocNone); + + // x.org will BadMatch if we don't set a border when the depth isn't the + // same as the parent depth. + attribute_mask |= CWBorderPixel; + swa.border_pixel = 0; + + use_argb_visual_ = true; + } + } + bounds_ = params.bounds; xwindow_ = XCreateWindow( xdisplay_, x_root_window_, bounds_.x(), bounds_.y(), bounds_.width(), bounds_.height(), 0, // border width - CopyFromParent, // depth + depth, InputOutput, - CopyFromParent, // visual + visual, attribute_mask, &swa); - base::MessagePumpX11::Current()->AddDispatcherForWindow(this, xwindow_); + if (ui::PlatformEventSource::GetInstance()) + ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); open_windows().push_back(xwindow_); // TODO(erg): Maybe need to set a ViewProp here like in RWHL::RWHL(). @@ -1014,9 +1112,10 @@ void DesktopRootWindowHostX11::InitX11Window( // List of window state properties (_NET_WM_STATE) to set, if any. std::vector< ::Atom> state_atom_list; - // Remove popup windows from taskbar. - if (params.type == Widget::InitParams::TYPE_POPUP || - params.type == Widget::InitParams::TYPE_BUBBLE) { + // Remove popup windows from taskbar unless overridden. + if ((params.type == Widget::InitParams::TYPE_POPUP || + params.type == Widget::InitParams::TYPE_BUBBLE) && + !params.force_show_in_taskbar) { state_atom_list.push_back( atom_cache_.GetAtom("_NET_WM_STATE_SKIP_TASKBAR")); } @@ -1027,6 +1126,11 @@ void DesktopRootWindowHostX11::InitX11Window( if (is_always_on_top_) state_atom_list.push_back(atom_cache_.GetAtom("_NET_WM_STATE_ABOVE")); + if (params.visible_on_all_workspaces) { + state_atom_list.push_back(atom_cache_.GetAtom("_NET_WM_STATE_STICKY")); + ui::SetIntProperty(xwindow_, "_NET_WM_DESKTOP", "CARDINAL", kAllDesktops); + } + // 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. @@ -1048,12 +1152,19 @@ void DesktopRootWindowHostX11::InitX11Window( std::string(kX11WindowRolePopup) : params.wm_role_name); } + if (params.remove_standard_frame) { + // Setting _GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED tells gnome-shell to not force + // fullscreen on the window when it matches the desktop size. + ui::SetHideTitlebarWhenMaximizedProperty(xwindow_, + ui::HIDE_TITLEBAR_WHEN_MAXIMIZED); + } + // If we have a parent, record the parent/child relationship. We use this // data during destruction to make sure that when we try to close a parent // window, we also destroy all child windows. - if (params.parent && params.parent->GetDispatcher()) { + if (params.parent && params.parent->GetHost()) { XID parent_xid = - params.parent->GetDispatcher()->host()->GetAcceleratedWidget(); + params.parent->GetHost()->GetAcceleratedWidget(); window_parent_ = GetHostForXID(parent_xid); DCHECK(window_parent_); window_parent_->window_children_.insert(this); @@ -1066,16 +1177,105 @@ void DesktopRootWindowHostX11::InitX11Window( if (window_icon) { SetWindowIcons(gfx::ImageSkia(), *window_icon); } + CreateCompositor(GetAcceleratedWidget()); +} + +void DesktopWindowTreeHostX11::OnWMStateUpdated() { + std::vector< ::Atom> atom_list; + if (!ui::GetAtomArrayProperty(xwindow_, "_NET_WM_STATE", &atom_list)) + return; + + bool was_minimized = IsMinimized(); + + window_properties_.clear(); + std::copy(atom_list.begin(), atom_list.end(), + inserter(window_properties_, window_properties_.begin())); + + // Propagate the window minimization information to the content window, so + // the render side can update its visibility properly. OnWMStateUpdated() is + // called by PropertyNofify event from DispatchEvent() when the browser is + // minimized or shown from minimized state. On Windows, this is realized by + // calling OnHostResized() with an empty size. In particular, + // HWNDMessageHandler::GetClientAreaBounds() returns an empty size when the + // window is minimized. On Linux, returning empty size in GetBounds() or + // SetBounds() does not work. + bool is_minimized = IsMinimized(); + if (is_minimized != was_minimized) { + if (is_minimized) + content_window_->Hide(); + else + content_window_->Show(); + } + + if (restored_bounds_.IsEmpty()) { + DCHECK(!IsFullscreen()); + if (IsMaximized()) { + // 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_; + } + } else if (!IsMaximized() && !IsFullscreen()) { + // If we have restored bounds, but WM_STATE no longer claims to be + // maximized or fullscreen, we should clear our restored bounds. + restored_bounds_ = gfx::Rect(); + } + + // Ignore requests by the window manager to enter or exit fullscreen (e.g. as + // a result of pressing a window manager accelerator key). Chrome does not + // handle window manager initiated fullscreen. In particular, Chrome needs to + // do preprocessing before the x window's fullscreen state is toggled. + + is_always_on_top_ = HasWMSpecProperty("_NET_WM_STATE_ABOVE"); + + // 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.) + Relayout(); + ResetWindowRegion(); +} + +void DesktopWindowTreeHostX11::OnFrameExtentsUpdated() { + std::vector<int> insets; + if (ui::GetIntArrayProperty(xwindow_, "_NET_FRAME_EXTENTS", &insets) && + insets.size() == 4) { + // |insets| are returned in the order: [left, right, top, bottom]. + native_window_frame_borders_ = gfx::Insets( + insets[2], + insets[0], + insets[3], + insets[1]); + } else { + native_window_frame_borders_ = gfx::Insets(); + } } -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 DesktopWindowTreeHostX11::UpdateWMUserTime( + const ui::PlatformEvent& event) { + if (!IsActive()) + return; + + ui::EventType type = ui::EventTypeFromNative(event); + if (type == ui::ET_MOUSE_PRESSED || + type == ui::ET_KEY_PRESSED || + type == ui::ET_TOUCH_PRESSED) { + unsigned long wm_user_time_ms = static_cast<unsigned long>( + ui::EventTimeFromNative(event).InMilliseconds()); + XChangeProperty(xdisplay_, + xwindow_, + atom_cache_.GetAtom("_NET_WM_USER_TIME"), + XA_CARDINAL, + 32, + PropModeReplace, + reinterpret_cast<const unsigned char *>(&wm_user_time_ms), + 1); + X11DesktopHandler::get()->set_wm_user_time_ms(wm_user_time_ms); + } } -void DesktopRootWindowHostX11::SetWMSpecState(bool enabled, +void DesktopWindowTreeHostX11::SetWMSpecState(bool enabled, ::Atom state1, ::Atom state2) { XEvent xclient; @@ -1096,49 +1296,88 @@ void DesktopRootWindowHostX11::SetWMSpecState(bool enabled, &xclient); } -bool DesktopRootWindowHostX11::HasWMSpecProperty(const char* property) const { +bool DesktopWindowTreeHostX11::HasWMSpecProperty(const char* property) const { return window_properties_.find(atom_cache_.GetAtom(property)) != window_properties_.end(); } -void DesktopRootWindowHostX11::OnCaptureReleased() { +void DesktopWindowTreeHostX11::SetUseNativeFrame(bool use_native_frame) { + use_native_frame_ = use_native_frame; + ui::SetUseOSWindowFrame(xwindow_, use_native_frame); + ResetWindowRegion(); +} + +void DesktopWindowTreeHostX11::OnCaptureReleased() { + x11_capture_.reset(); g_current_capture = NULL; - delegate_->OnHostLostWindowCapture(); + OnHostLostWindowCapture(); native_widget_delegate_->OnMouseCaptureLost(); } -void DesktopRootWindowHostX11::DispatchMouseEvent(ui::MouseEvent* event) { +void DesktopWindowTreeHostX11::DispatchMouseEvent(ui::MouseEvent* event) { + // In Windows, the native events sent to chrome are separated into client + // and non-client versions of events, which we record on our LocatedEvent + // structures. On X11, we emulate the concept of non-client. Before we pass + // this event to the cross platform event handling framework, we need to + // make sure it is appropriately marked as non-client if it's in the non + // client area, or otherwise, we can get into a state where the a window is + // set as the |mouse_pressed_handler_| in window_event_dispatcher.cc + // despite the mouse button being released. + // + // We can't do this later in the dispatch process because we share that + // with ash, and ash gets confused about event IS_NON_CLIENT-ness on + // events, since ash doesn't expect this bit to be set, because it's never + // been set before. (This works on ash on Windows because none of the mouse + // events on the ash desktop are clicking in what Windows considers to be a + // non client area.) Likewise, we won't want to do the following in any + // WindowTreeHost that hosts ash. + if (content_window_ && content_window_->delegate()) { + int flags = event->flags(); + int hit_test_code = + content_window_->delegate()->GetNonClientComponent(event->location()); + if (hit_test_code != HTCLIENT && hit_test_code != HTNOWHERE) + flags |= ui::EF_IS_NON_CLIENT; + event->set_flags(flags); + } + + // While we unset the urgency hint when we gain focus, we also must remove it + // on mouse clicks because we can call FlashFrame() on an active window. + if (event->IsAnyButton() || event->IsMouseWheelEvent()) + FlashFrame(false); + if (!g_current_capture || g_current_capture == this) { - delegate_->OnHostMouseEvent(event); + SendEventToProcessor(event); } else { - // Another DesktopRootWindowHostX11 has installed itself as + // Another DesktopWindowTreeHostX11 has installed itself as // capture. Translate the event's location and dispatch to the other. - event->ConvertLocationToTarget(root_window_->window(), - g_current_capture->root_window_->window()); - g_current_capture->delegate_->OnHostMouseEvent(event); + event->ConvertLocationToTarget(window(), g_current_capture->window()); + g_current_capture->SendEventToProcessor(event); } } -void DesktopRootWindowHostX11::DispatchTouchEvent(ui::TouchEvent* event) { +void DesktopWindowTreeHostX11::DispatchTouchEvent(ui::TouchEvent* event) { if (g_current_capture && g_current_capture != this && event->type() == ui::ET_TOUCH_PRESSED) { - event->ConvertLocationToTarget(root_window_->window(), - g_current_capture->root_window_->window()); - g_current_capture->delegate_->OnHostTouchEvent(event); + event->ConvertLocationToTarget(window(), g_current_capture->window()); + g_current_capture->SendEventToProcessor(event); } else { - delegate_->OnHostTouchEvent(event); + SendEventToProcessor(event); } } -void DesktopRootWindowHostX11::ResetWindowRegion() { +void DesktopWindowTreeHostX11::ResetWindowRegion() { // If a custom window shape was supplied then apply it. if (custom_window_shape_) { XShapeCombineRegion( - xdisplay_, xwindow_, ShapeBounding, 0, 0, custom_window_shape_, false); + xdisplay_, xwindow_, ShapeBounding, 0, 0, window_shape_, false); return; } - if (!IsMaximized()) { + if (window_shape_) + XDestroyRegion(window_shape_); + window_shape_ = NULL; + + if (!IsMaximized() && !IsFullscreen()) { gfx::Path window_mask; views::Widget* widget = native_widget_delegate_->AsWidget(); if (widget->non_client_view()) { @@ -1146,24 +1385,36 @@ void DesktopRootWindowHostX11::ResetWindowRegion() { // so, use it to define the window shape. If not, fall through. widget->non_client_view()->GetWindowMask(bounds_.size(), &window_mask); if (window_mask.countPoints() > 0) { - Region region = gfx::CreateRegionFromSkPath(window_mask); + window_shape_ = gfx::CreateRegionFromSkPath(window_mask); XShapeCombineRegion(xdisplay_, xwindow_, ShapeBounding, - 0, 0, region, false); - XDestroyRegion(region); + 0, 0, window_shape_, false); return; } } } - // If we didn't set the shape for any reason, reset the shaping information - // by ShapeSet-ing with our bounds rect. - XRectangle r = { 0, 0, static_cast<unsigned short>(bounds_.width()), - static_cast<unsigned short>(bounds_.height()) }; - XShapeCombineRectangles(xdisplay_, xwindow_, ShapeBounding, - 0, 0, &r, 1, ShapeSet, YXBanded); + // If we didn't set the shape for any reason, reset the shaping information. + // How this is done depends on the border style, due to quirks and bugs in + // various window managers. + if (ShouldUseNativeFrame()) { + // If the window has system borders, the mask must be set to null (not a + // rectangle), because several window managers (eg, KDE, XFCE, XMonad) will + // not put borders on a window with a custom shape. + XShapeCombineMask(xdisplay_, xwindow_, ShapeBounding, 0, 0, None, ShapeSet); + } else { + // Conversely, if the window does not have system borders, the mask must be + // manually set to a rectangle that covers the whole window (not null). This + // is due to a bug in KWin <= 4.11.5 (KDE bug #330573) where setting a null + // shape causes the hint to disable system borders to be ignored (resulting + // in a double border). + XRectangle r = {0, 0, static_cast<unsigned short>(bounds_.width()), + static_cast<unsigned short>(bounds_.height())}; + XShapeCombineRectangles( + xdisplay_, xwindow_, ShapeBounding, 0, 0, &r, 1, ShapeSet, YXBanded); + } } -void DesktopRootWindowHostX11::SerializeImageRepresentation( +void DesktopWindowTreeHostX11::SerializeImageRepresentation( const gfx::ImageSkiaRep& rep, std::vector<unsigned long>* data) { int width = rep.GetWidth(); @@ -1180,28 +1431,135 @@ void DesktopRootWindowHostX11::SerializeImageRepresentation( data->push_back(bitmap.getColor(x, y)); } -std::list<XID>& DesktopRootWindowHostX11::open_windows() { +Visual* DesktopWindowTreeHostX11::GetARGBVisual() { + XVisualInfo visual_template; + visual_template.screen = 0; + Visual* to_return = NULL; + + int visuals_len; + XVisualInfo* visual_list = XGetVisualInfo(xdisplay_, + VisualScreenMask, + &visual_template, &visuals_len); + for (int i = 0; i < visuals_len; ++i) { + // Why support only 8888 ARGB? Because it's all that GTK+ supports. In + // gdkvisual-x11.cc, they look for this specific visual and use it for all + // their alpha channel using needs. + // + // TODO(erg): While the following does find a valid visual, some GL drivers + // don't believe that this has an alpha channel. According to marcheu@, + // this should work on open source driver though. (It doesn't work with + // NVidia's binaries currently.) http://crbug.com/369209 + if (visual_list[i].depth == 32 && + visual_list[i].visual->red_mask == 0xff0000 && + visual_list[i].visual->green_mask == 0x00ff00 && + visual_list[i].visual->blue_mask == 0x0000ff) { + to_return = visual_list[i].visual; + break; + } + } + + if (visual_list) + XFree(visual_list); + + return to_return; +} + +std::list<XID>& DesktopWindowTreeHostX11::open_windows() { if (!open_windows_) open_windows_ = new std::list<XID>(); return *open_windows_; } +void DesktopWindowTreeHostX11::MapWindow(ui::WindowShowState show_state) { + if (show_state != ui::SHOW_STATE_DEFAULT && + show_state != ui::SHOW_STATE_NORMAL && + show_state != ui::SHOW_STATE_INACTIVE) { + // It will behave like SHOW_STATE_NORMAL. + NOTIMPLEMENTED(); + } + + // 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); + + // If SHOW_STATE_INACTIVE, tell the window manager not to focus the window + // when mapping. This is done by setting the _NET_WM_USER_TIME to 0. See e.g. + // http://standards.freedesktop.org/wm-spec/latest/ar01s05.html + unsigned long wm_user_time_ms = (show_state == ui::SHOW_STATE_INACTIVE) ? + 0 : X11DesktopHandler::get()->wm_user_time_ms(); + if (show_state == ui::SHOW_STATE_INACTIVE || wm_user_time_ms != 0) { + XChangeProperty(xdisplay_, + xwindow_, + atom_cache_.GetAtom("_NET_WM_USER_TIME"), + XA_CARDINAL, + 32, + PropModeReplace, + reinterpret_cast<const unsigned char *>(&wm_user_time_ms), + 1); + } + + 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. + if (ui::X11EventSource::GetInstance()) + ui::X11EventSource::GetInstance()->BlockUntilWindowMapped(xwindow_); + window_mapped_ = true; +} + +void DesktopWindowTreeHostX11::SetWindowTransparency() { + compositor()->SetHostHasTransparentBackground(use_argb_visual_); + window()->SetTransparent(use_argb_visual_); + content_window_->SetTransparent(use_argb_visual_); +} + +void DesktopWindowTreeHostX11::Relayout() { + 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(); +} + //////////////////////////////////////////////////////////////////////////////// -// DesktopRootWindowHostX11, MessageLoop::Dispatcher implementation: +// DesktopWindowTreeHostX11, ui::PlatformEventDispatcher implementation: + +bool DesktopWindowTreeHostX11::CanDispatchEvent( + const ui::PlatformEvent& event) { + return event->xany.window == xwindow_ || + (event->type == GenericEvent && + static_cast<XIDeviceEvent*>(event->xcookie.data)->event == xwindow_); +} -bool DesktopRootWindowHostX11::Dispatch(const base::NativeEvent& event) { +uint32_t DesktopWindowTreeHostX11::DispatchEvent( + const ui::PlatformEvent& event) { XEvent* xev = event; - TRACE_EVENT1("views", "DesktopRootWindowHostX11::Dispatch", + TRACE_EVENT1("views", "DesktopWindowTreeHostX11::Dispatch", "event->type", event->type); + UpdateWMUserTime(event); + // May want to factor CheckXEventForConsistency(xev); into a common location // since it is called here. switch (xev->type) { case EnterNotify: case LeaveNotify: { - if (!g_current_capture) - X11DesktopHandler::get()->ProcessXEvent(xev); + // Ignore EventNotify and LeaveNotify events from children of |xwindow_|. + // NativeViewGLSurfaceGLX adds a child to |xwindow_|. + // TODO(pkotwicz|tdanderson): Figure out whether the suppression is + // necessary. crbug.com/385716 + if (xev->xcrossing.detail == NotifyInferior) + break; + ui::MouseEvent mouse_event(xev); DispatchMouseEvent(&mouse_event); break; @@ -1209,33 +1567,20 @@ bool DesktopRootWindowHostX11::Dispatch(const base::NativeEvent& event) { case Expose: { gfx::Rect damage_rect(xev->xexpose.x, xev->xexpose.y, xev->xexpose.width, xev->xexpose.height); - delegate_->OnHostPaint(damage_rect); + compositor()->ScheduleRedrawRect(damage_rect); break; } case KeyPress: { ui::KeyEvent keydown_event(xev, false); - delegate_->OnHostKeyEvent(&keydown_event); + SendEventToProcessor(&keydown_event); break; } case KeyRelease: { ui::KeyEvent keyup_event(xev, false); - delegate_->OnHostKeyEvent(&keyup_event); + SendEventToProcessor(&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_->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 ButtonPress: case ButtonRelease: { ui::EventType event_type = ui::EventTypeFromNative(xev); switch (event_type) { @@ -1261,11 +1606,15 @@ bool DesktopRootWindowHostX11::Dispatch(const base::NativeEvent& event) { case FocusOut: if (xev->xfocus.mode != NotifyGrab) { ReleaseCapture(); - delegate_->OnHostLostWindowCapture(); + OnHostLostWindowCapture(); + X11DesktopHandler::get()->ProcessXEvent(xev); } else { - delegate_->OnHostLostMouseGrab(); + dispatcher()->OnHostLostMouseGrab(); } break; + case FocusIn: + X11DesktopHandler::get()->ProcessXEvent(xev); + break; case ConfigureNotify: { DCHECK_EQ(xwindow_, xev->xconfigure.window); DCHECK_EQ(xwindow_, xev->xconfigure.event); @@ -1285,11 +1634,18 @@ bool DesktopRootWindowHostX11::Dispatch(const base::NativeEvent& event) { bool origin_changed = bounds_.origin() != bounds.origin(); previous_bounds_ = bounds_; bounds_ = bounds; - if (size_changed) - delegate_->OnHostResized(bounds.size()); + if (origin_changed) - delegate_->OnHostMoved(bounds_.origin()); - ResetWindowRegion(); + OnHostMoved(bounds_.origin()); + + if (size_changed) { + delayed_resize_task_.Reset(base::Bind( + &DesktopWindowTreeHostX11::DelayedResize, + close_widget_factory_.GetWeakPtr(), + bounds.size())); + base::MessageLoop::current()->PostTask( + FROM_HERE, delayed_resize_task_.callback()); + } break; } case GenericEvent: { @@ -1325,34 +1681,6 @@ bool DesktopRootWindowHostX11::Dispatch(const base::NativeEvent& event) { 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( - delegate_->AsRootWindow()->window()); - 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); @@ -1367,7 +1695,7 @@ bool DesktopRootWindowHostX11::Dispatch(const base::NativeEvent& event) { case ui::ET_SCROLL_FLING_CANCEL: case ui::ET_SCROLL: { ui::ScrollEvent scrollev(xev); - delegate_->OnHostScrollEvent(&scrollev); + SendEventToProcessor(&scrollev); break; } case ui::ET_UNKNOWN: @@ -1382,13 +1710,13 @@ bool DesktopRootWindowHostX11::Dispatch(const base::NativeEvent& event) { break; } case MapNotify: { - FOR_EACH_OBSERVER(DesktopRootWindowHostObserverX11, + FOR_EACH_OBSERVER(DesktopWindowTreeHostObserverX11, observer_list_, OnWindowMapped(xwindow_)); break; } case UnmapNotify: { - FOR_EACH_OBSERVER(DesktopRootWindowHostObserverX11, + FOR_EACH_OBSERVER(DesktopWindowTreeHostObserverX11, observer_list_, OnWindowUnmapped(xwindow_)); break; @@ -1399,7 +1727,7 @@ bool DesktopRootWindowHostX11::Dispatch(const base::NativeEvent& event) { 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(); + OnHostCloseRequested(); } else if (protocol == atom_cache_.GetAtom("_NET_WM_PING")) { XEvent reply_event = *xev; reply_event.xclient.window = x_root_window_; @@ -1430,7 +1758,6 @@ bool DesktopRootWindowHostX11::Dispatch(const base::NativeEvent& event) { case MappingModifier: case MappingKeyboard: XRefreshKeyboardMapping(&xev->xmapping); - root_window_->OnKeyboardMappingChanged(); break; case MappingPointer: ui::DeviceDataManager::GetInstance()->UpdateButtonMap(); @@ -1464,50 +1791,11 @@ bool DesktopRootWindowHostX11::Dispatch(const base::NativeEvent& event) { 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_; - } - - is_fullscreen_ = HasWMSpecProperty("_NET_WM_STATE_FULLSCREEN"); - is_always_on_top_ = HasWMSpecProperty("_NET_WM_STATE_ABOVE"); - - // 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(); - } + ::Atom changed_atom = xev->xproperty.atom; + if (changed_atom == atom_cache_.GetAtom("_NET_WM_STATE")) + OnWMStateUpdated(); + else if (changed_atom == atom_cache_.GetAtom("_NET_FRAME_EXTENTS")) + OnFrameExtentsUpdated(); break; } case SelectionNotify: { @@ -1515,25 +1803,31 @@ bool DesktopRootWindowHostX11::Dispatch(const base::NativeEvent& event) { break; } } - return true; + return ui::POST_DISPATCH_STOP_PROPAGATION; +} + +void DesktopWindowTreeHostX11::DelayedResize(const gfx::Size& size) { + OnHostResized(size); + ResetWindowRegion(); + delayed_resize_task_.Cancel(); } //////////////////////////////////////////////////////////////////////////////// -// DesktopRootWindowHost, public: +// DesktopWindowTreeHost, public: // static -DesktopRootWindowHost* DesktopRootWindowHost::Create( +DesktopWindowTreeHost* DesktopWindowTreeHost::Create( internal::NativeWidgetDelegate* native_widget_delegate, DesktopNativeWidgetAura* desktop_native_widget_aura) { - return new DesktopRootWindowHostX11(native_widget_delegate, + return new DesktopWindowTreeHostX11(native_widget_delegate, desktop_native_widget_aura); } // static -ui::NativeTheme* DesktopRootWindowHost::GetNativeTheme(aura::Window* window) { +ui::NativeTheme* DesktopWindowTreeHost::GetNativeTheme(aura::Window* window) { const views::LinuxUI* linux_ui = views::LinuxUI::instance(); if (linux_ui) { - ui::NativeTheme* native_theme = linux_ui->GetNativeTheme(); + ui::NativeTheme* native_theme = linux_ui->GetNativeTheme(window); if (native_theme) return native_theme; } diff --git a/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_x11.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h index 76a0821a6de..a67a1516820 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_root_window_host_x11.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h @@ -2,52 +2,59 @@ // 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_ +#ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_WINDOW_TREE_HOST_X11_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_WINDOW_TREE_HOST_X11_H_ #include <X11/extensions/shape.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/cancelable_callback.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "ui/aura/window_tree_host.h" #include "ui/base/cursor/cursor_loader_x11.h" +#include "ui/events/event_source.h" +#include "ui/events/platform/platform_event_dispatcher.h" +#include "ui/gfx/insets.h" #include "ui/gfx/rect.h" #include "ui/gfx/x/x11_atom_cache.h" #include "ui/views/views_export.h" -#include "ui/views/widget/desktop_aura/desktop_root_window_host.h" +#include "ui/views/widget/desktop_aura/desktop_window_tree_host.h" namespace gfx { class ImageSkia; class ImageSkiaRep; } +namespace ui { +class EventHandler; +} + namespace views { class DesktopDragDropClientAuraX11; class DesktopDispatcherClient; -class DesktopRootWindowHostObserverX11; +class DesktopWindowTreeHostObserverX11; class X11DesktopWindowMoveClient; +class X11ScopedCapture; class X11WindowEventFilter; -class VIEWS_EXPORT DesktopRootWindowHostX11 : - public DesktopRootWindowHost, - public aura::RootWindowHost, - public base::MessageLoop::Dispatcher { +class VIEWS_EXPORT DesktopWindowTreeHostX11 + : public DesktopWindowTreeHost, + public aura::WindowTreeHost, + public ui::EventSource, + public ui::PlatformEventDispatcher { public: - DesktopRootWindowHostX11( + DesktopWindowTreeHostX11( internal::NativeWidgetDelegate* native_widget_delegate, DesktopNativeWidgetAura* desktop_native_widget_aura); - virtual ~DesktopRootWindowHostX11(); + virtual ~DesktopWindowTreeHostX11(); // 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); + static DesktopWindowTreeHostX11* GetHostForXID(XID xid); // Get all open top-level windows. This includes windows that may not be // visible. This list is sorted in their stacking order, i.e. the first window @@ -57,29 +64,38 @@ class VIEWS_EXPORT DesktopRootWindowHostX11 : // Returns the current bounds in terms of the X11 Root Window. gfx::Rect GetX11RootWindowBounds() const; + // Returns the current bounds in terms of the X11 Root Window including the + // borders provided by the window manager (if any). + gfx::Rect GetX11RootWindowOuterBounds() const; + + // Returns the window shape if the window is not rectangular. Returns NULL + // otherwise. + ::Region GetWindowShape() const; + // 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); + void AddObserver(views::DesktopWindowTreeHostObserverX11* observer); + void RemoveObserver(views::DesktopWindowTreeHostObserverX11* observer); + + // Swaps the current handler for events in the non client view with |handler|. + void SwapNonClientEventHandler(scoped_ptr<ui::EventHandler> handler); // Deallocates the internal list of open windows. static void CleanUpWindowList(); protected: - // Overridden from DesktopRootWindowHost: + // Overridden from DesktopWindowTreeHost: virtual void Init(aura::Window* content_window, - const Widget::InitParams& params, - aura::RootWindow::CreateParams* rw_create_params) OVERRIDE; - virtual void OnRootWindowCreated(aura::RootWindow* root, - const Widget::InitParams& params) OVERRIDE; + const Widget::InitParams& params) OVERRIDE; + virtual void OnNativeWidgetCreated(const Widget::InitParams& params) OVERRIDE; virtual scoped_ptr<corewm::Tooltip> CreateTooltip() OVERRIDE; virtual scoped_ptr<aura::client::DragDropClient> CreateDragDropClient(DesktopNativeCursorManager* cursor_manager) OVERRIDE; virtual void Close() OVERRIDE; virtual void CloseNow() OVERRIDE; - virtual aura::RootWindowHost* AsRootWindowHost() OVERRIDE; + virtual aura::WindowTreeHost* AsWindowTreeHost() OVERRIDE; virtual void ShowWindowWithState(ui::WindowShowState show_state) OVERRIDE; virtual void ShowMaximizedWithBounds( const gfx::Rect& restored_bounds) OVERRIDE; @@ -106,7 +122,8 @@ class VIEWS_EXPORT DesktopRootWindowHostX11 : virtual bool HasCapture() const OVERRIDE; virtual void SetAlwaysOnTop(bool always_on_top) OVERRIDE; virtual bool IsAlwaysOnTop() const OVERRIDE; - virtual bool SetWindowTitle(const string16& title) OVERRIDE; + virtual void SetVisibleOnAllWorkspaces(bool always_visible) OVERRIDE; + virtual bool SetWindowTitle(const base::string16& title) OVERRIDE; virtual void ClearNativeFocus() OVERRIDE; virtual Widget::MoveLoopResult RunMoveLoop( const gfx::Vector2d& drag_offset, @@ -114,9 +131,9 @@ class VIEWS_EXPORT DesktopRootWindowHostX11 : Widget::MoveLoopEscapeBehavior escape_behavior) OVERRIDE; virtual void EndMoveLoop() OVERRIDE; virtual void SetVisibilityChangedAnimationsEnabled(bool value) OVERRIDE; - virtual bool ShouldUseNativeFrame() OVERRIDE; + virtual bool ShouldUseNativeFrame() const OVERRIDE; + virtual bool ShouldWindowContentsBeTransparent() const 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; @@ -128,43 +145,44 @@ class VIEWS_EXPORT DesktopRootWindowHostX11 : virtual void OnNativeWidgetFocus() OVERRIDE; virtual void OnNativeWidgetBlur() OVERRIDE; virtual bool IsAnimatingClosed() const OVERRIDE; + virtual bool IsTranslucentWindowOpacitySupported() const OVERRIDE; - // Overridden from aura::RootWindowHost: - virtual aura::RootWindow* GetRootWindow() OVERRIDE; + // Overridden from aura::WindowTreeHost: + virtual ui::EventSource* GetEventSource() 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 PostNativeEvent(const base::NativeEvent& native_event) OVERRIDE; virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE; - virtual void PrepareForShutdown() OVERRIDE; + virtual void SetCursorNative(gfx::NativeCursor cursor) OVERRIDE; + virtual void MoveCursorToNative(const gfx::Point& location) OVERRIDE; + virtual void OnCursorVisibilityChangedNative(bool show) OVERRIDE; -private: + // Overridden frm ui::EventSource + virtual ui::EventProcessor* GetEventProcessor() 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); + // Creates an aura::WindowEventDispatcher to contain the |content_window|, + // along with all aura client objects that direct behavior. + aura::WindowEventDispatcher* InitDispatcher(const Widget::InitParams& params); + + // Called when |xwindow_|'s _NET_WM_STATE property is updated. + void OnWMStateUpdated(); + + // Called when |xwindow_|'s _NET_FRAME_EXTENTS property is updated. + void OnFrameExtentsUpdated(); - // 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(); + // Updates |xwindow_|'s _NET_WM_USER_TIME if |xwindow_| is active. + void UpdateWMUserTime(const ui::PlatformEvent& event); // Sends a message to the x11 window manager, enabling or disabling the // states |state1| and |state2|. @@ -173,6 +191,9 @@ private: // Checks if the window manager has set a specific state. bool HasWMSpecProperty(const char* property) const; + // Sets whether the window's borders are provided by the window manager. + void SetUseNativeFrame(bool use_native_frame); + // Called when another DRWHL takes capture, or when capture is released // entirely. void OnCaptureReleased(); @@ -194,13 +215,28 @@ private: void SerializeImageRepresentation(const gfx::ImageSkiaRep& rep, std::vector<unsigned long>* data); + // Returns an 8888 ARGB visual. Can return NULL if there is no matching + // visual on this display. + Visual* GetARGBVisual(); + // See comment for variable open_windows_. static std::list<XID>& open_windows(); - // Overridden from Dispatcher: - virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE; + // Map the window (shows it) taking into account the given |show_state|. + void MapWindow(ui::WindowShowState show_state); - base::WeakPtrFactory<DesktopRootWindowHostX11> close_widget_factory_; + void SetWindowTransparency(); + + // Relayout the widget's client and non-client views. + void Relayout(); + + // ui::PlatformEventDispatcher: + virtual bool CanDispatchEvent(const ui::PlatformEvent& event) OVERRIDE; + virtual uint32_t DispatchEvent(const ui::PlatformEvent& event) OVERRIDE; + + void DelayedResize(const gfx::Size& size); + + base::WeakPtrFactory<DesktopWindowTreeHostX11> close_widget_factory_; // X11 things // The display and the native X window hosting the root window. @@ -232,15 +268,17 @@ private: // The window manager state bits. std::set< ::Atom> window_properties_; - // Local flag for fullscreen state to avoid a state mismatch between - // server and local window_properties_ during app-initiated fullscreen. + // Whether |xwindow_| was requested to be fullscreen via SetFullscreen(). bool is_fullscreen_; // True if the window should stay on top of most other windows. bool is_always_on_top_; - // We are owned by the RootWindow, but we have to have a back pointer to it. - aura::RootWindow* root_window_; + // True if the window has title-bar / borders provided by the window manager. + bool use_native_frame_; + + // Whether we used an ARGB visual for our window. + bool use_argb_visual_; scoped_ptr<DesktopDispatcherClient> dispatcher_client_; @@ -249,7 +287,7 @@ private: // Current Aura cursor. gfx::NativeCursor current_cursor_; - scoped_ptr<X11WindowEventFilter> x11_window_event_filter_; + scoped_ptr<ui::EventHandler> x11_non_client_event_filter_; scoped_ptr<X11DesktopWindowMoveClient> x11_window_move_client_; // TODO(beng): Consider providing an interface to DesktopNativeWidgetAura @@ -262,30 +300,46 @@ private: // We can optionally have a parent which can order us to close, or own // children who we're responsible for closing when we CloseNow(). - DesktopRootWindowHostX11* window_parent_; - std::set<DesktopRootWindowHostX11*> window_children_; + DesktopWindowTreeHostX11* window_parent_; + std::set<DesktopWindowTreeHostX11*> window_children_; - ObserverList<DesktopRootWindowHostObserverX11> observer_list_; + ObserverList<DesktopWindowTreeHostObserverX11> observer_list_; - // Copy of custom window shape specified via SetShape(), if any. - ::Region custom_window_shape_; + // The window shape if the window is non-rectangular. + ::Region window_shape_; + + // Whether |window_shape_| was set via SetShape(). + bool custom_window_shape_; + + // The size of the window manager provided borders (if any). + gfx::Insets native_window_frame_borders_; // 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; + static DesktopWindowTreeHostX11* g_current_capture; // A list of all (top-level) windows that have been created but not yet // destroyed. static std::list<XID>* open_windows_; - string16 window_title_; + scoped_ptr<X11ScopedCapture> x11_capture_; + + base::string16 window_title_; + + // Whether we currently are flashing our frame. This feature is implemented + // by setting the urgency hint with the window manager, which can draw + // attention to the window or completely ignore the hint. We stop flashing + // the frame when |xwindow_| gains focus or handles a mouse button event. + bool urgency_hint_set_; + + base::CancelableCallback<void()> delayed_resize_task_; - DISALLOW_COPY_AND_ASSIGN(DesktopRootWindowHostX11); + DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostX11); }; } // namespace views -#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_ROOT_WINDOW_HOST_X11_H_ +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_WINDOW_TREE_HOST_X11_H_ diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc new file mode 100644 index 00000000000..8a66c6e0327 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc @@ -0,0 +1,448 @@ +// Copyright 2014 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 <vector> + +#include <X11/extensions/shape.h> +#include <X11/Xlib.h> + +// Get rid of X11 macros which conflict with gtest. +#undef Bool +#undef None + +#include "base/memory/scoped_ptr.h" +#include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" +#include "ui/base/hit_test.h" +#include "ui/base/x/x11_util.h" +#include "ui/events/platform/x11/x11_event_source.h" +#include "ui/gfx/path.h" +#include "ui/gfx/point.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/x/x11_atom_cache.h" +#include "ui/views/test/views_test_base.h" +#include "ui/views/test/x11_property_change_waiter.h" +#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" +#include "ui/views/widget/widget_delegate.h" +#include "ui/views/window/non_client_view.h" + +namespace views { + +namespace { + +// Blocks till the window state hint, |hint|, is set or unset. +class WMStateWaiter : public X11PropertyChangeWaiter { + public: + WMStateWaiter(XID window, + const char* hint, + bool wait_till_set) + : X11PropertyChangeWaiter(window, "_NET_WM_STATE"), + hint_(hint), + wait_till_set_(wait_till_set) { + + const char* kAtomsToCache[] = { + hint, + NULL + }; + atom_cache_.reset(new ui::X11AtomCache(gfx::GetXDisplay(), kAtomsToCache)); + } + + virtual ~WMStateWaiter() { + } + + private: + // X11PropertyChangeWaiter: + virtual bool ShouldKeepOnWaiting(const ui::PlatformEvent& event) OVERRIDE { + std::vector<Atom> hints; + if (ui::GetAtomArrayProperty(xwindow(), "_NET_WM_STATE", &hints)) { + std::vector<Atom>::iterator it = std::find( + hints.begin(), + hints.end(), + atom_cache_->GetAtom(hint_)); + bool hint_set = (it != hints.end()); + return hint_set != wait_till_set_; + } + return true; + } + + scoped_ptr<ui::X11AtomCache> atom_cache_; + + // The name of the hint to wait to get set or unset. + const char* hint_; + + // Whether we are waiting for |hint| to be set or unset. + bool wait_till_set_; + + DISALLOW_COPY_AND_ASSIGN(WMStateWaiter); +}; + +// A NonClientFrameView with a window mask with the bottom right corner cut out. +class ShapedNonClientFrameView : public NonClientFrameView { + public: + explicit ShapedNonClientFrameView() { + } + + virtual ~ShapedNonClientFrameView() { + } + + // NonClientFrameView: + virtual gfx::Rect GetBoundsForClientView() const OVERRIDE { + return bounds(); + } + virtual gfx::Rect GetWindowBoundsForClientBounds( + const gfx::Rect& client_bounds) const OVERRIDE { + return client_bounds; + } + virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE { + return HTNOWHERE; + } + virtual void GetWindowMask(const gfx::Size& size, + gfx::Path* window_mask) OVERRIDE { + int right = size.width(); + int bottom = size.height(); + + window_mask->moveTo(0, 0); + window_mask->lineTo(0, bottom); + window_mask->lineTo(right, bottom); + window_mask->lineTo(right, 10); + window_mask->lineTo(right - 10, 10); + window_mask->lineTo(right - 10, 0); + window_mask->close(); + } + virtual void ResetWindowControls() OVERRIDE { + } + virtual void UpdateWindowIcon() OVERRIDE { + } + virtual void UpdateWindowTitle() OVERRIDE { + } + + private: + DISALLOW_COPY_AND_ASSIGN(ShapedNonClientFrameView); +}; + +class ShapedWidgetDelegate : public WidgetDelegateView { + public: + ShapedWidgetDelegate() { + } + + virtual ~ShapedWidgetDelegate() { + } + + // WidgetDelegateView: + virtual NonClientFrameView* CreateNonClientFrameView( + Widget* widget) OVERRIDE { + return new ShapedNonClientFrameView; + } + + private: + DISALLOW_COPY_AND_ASSIGN(ShapedWidgetDelegate); +}; + +// Creates a widget of size 100x100. +scoped_ptr<Widget> CreateWidget(WidgetDelegate* delegate) { + scoped_ptr<Widget> widget(new Widget); + Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); + params.delegate = delegate; + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.remove_standard_frame = true; + params.native_widget = new DesktopNativeWidgetAura(widget.get()); + params.bounds = gfx::Rect(100, 100, 100, 100); + widget->Init(params); + return widget.Pass(); +} + +// Returns the list of rectangles which describe |xid|'s bounding region via the +// X shape extension. +std::vector<gfx::Rect> GetShapeRects(XID xid) { + int dummy; + int shape_rects_size; + XRectangle* shape_rects = XShapeGetRectangles(gfx::GetXDisplay(), + xid, + ShapeBounding, + &shape_rects_size, + &dummy); + + std::vector<gfx::Rect> shape_vector; + for (int i = 0; i < shape_rects_size; ++i) { + shape_vector.push_back(gfx::Rect( + shape_rects[i].x, + shape_rects[i].y, + shape_rects[i].width, + shape_rects[i].height)); + } + XFree(shape_rects); + return shape_vector; +} + +// Returns true if one of |rects| contains point (x,y). +bool ShapeRectContainsPoint(const std::vector<gfx::Rect>& shape_rects, + int x, + int y) { + gfx::Point point(x, y); + for (size_t i = 0; i < shape_rects.size(); ++i) { + if (shape_rects[i].Contains(point)) + return true; + } + return false; +} + +} // namespace + +class DesktopWindowTreeHostX11Test : public ViewsTestBase { + public: + DesktopWindowTreeHostX11Test() { + } + virtual ~DesktopWindowTreeHostX11Test() { + } + + virtual void SetUp() OVERRIDE { + ViewsTestBase::SetUp(); + + // Make X11 synchronous for our display connection. This does not force the + // window manager to behave synchronously. + XSynchronize(gfx::GetXDisplay(), True); + } + + virtual void TearDown() OVERRIDE { + XSynchronize(gfx::GetXDisplay(), False); + ViewsTestBase::TearDown(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostX11Test); +}; + +// Tests that the shape is properly set on the x window. +TEST_F(DesktopWindowTreeHostX11Test, Shape) { + if (!ui::IsShapeExtensionAvailable()) + return; + + // 1) Test setting the window shape via the NonClientFrameView. This technique + // is used to get rounded corners on Chrome windows when not using the native + // window frame. + scoped_ptr<Widget> widget1 = CreateWidget(new ShapedWidgetDelegate()); + widget1->Show(); + ui::X11EventSource::GetInstance()->DispatchXEvents(); + + XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget(); + std::vector<gfx::Rect> shape_rects = GetShapeRects(xid1); + ASSERT_FALSE(shape_rects.empty()); + + // The widget was supposed to be 100x100, but the WM might have ignored this + // suggestion. + int widget_width = widget1->GetWindowBoundsInScreen().width(); + EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, widget_width - 15, 5)); + EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, widget_width - 5, 5)); + EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, widget_width - 5, 15)); + EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, widget_width + 5, 15)); + + // Changing widget's size should update the shape. + widget1->SetBounds(gfx::Rect(100, 100, 200, 200)); + ui::X11EventSource::GetInstance()->DispatchXEvents(); + + if (widget1->GetWindowBoundsInScreen().width() == 200) { + shape_rects = GetShapeRects(xid1); + ASSERT_FALSE(shape_rects.empty()); + EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 85, 5)); + EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 5)); + EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 185, 5)); + EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 195, 5)); + EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 195, 15)); + EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 205, 15)); + } + + if (ui::WmSupportsHint(ui::GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"))) { + // The shape should be changed to a rectangle which fills the entire screen + // when |widget1| is maximized. + { + WMStateWaiter waiter(xid1, "_NET_WM_STATE_MAXIMIZED_VERT", true); + widget1->Maximize(); + waiter.Wait(); + } + + // xvfb does not support Xrandr so we cannot check the maximized window's + // bounds. + gfx::Rect maximized_bounds; + ui::GetWindowRect(xid1, &maximized_bounds); + + shape_rects = GetShapeRects(xid1); + ASSERT_FALSE(shape_rects.empty()); + EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, + maximized_bounds.width() - 1, + 5)); + EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, + maximized_bounds.width() - 1, + 15)); + } + + // 2) Test setting the window shape via Widget::SetShape(). + gfx::Path shape2; + shape2.moveTo(10, 0); + shape2.lineTo(10, 10); + shape2.lineTo(0, 10); + shape2.lineTo(0, 100); + shape2.lineTo(100, 100); + shape2.lineTo(100, 0); + shape2.close(); + + scoped_ptr<Widget> widget2(CreateWidget(NULL)); + widget2->Show(); + widget2->SetShape(shape2.CreateNativeRegion()); + ui::X11EventSource::GetInstance()->DispatchXEvents(); + + XID xid2 = widget2->GetNativeWindow()->GetHost()->GetAcceleratedWidget(); + shape_rects = GetShapeRects(xid2); + ASSERT_FALSE(shape_rects.empty()); + EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 5, 5)); + EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 15, 5)); + EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15)); + EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 105, 15)); + + // Changing the widget's size should not affect the shape. + widget2->SetBounds(gfx::Rect(100, 100, 200, 200)); + shape_rects = GetShapeRects(xid2); + ASSERT_FALSE(shape_rects.empty()); + EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 5, 5)); + EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 15, 5)); + EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15)); + EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 105, 15)); +} + +// Test that the widget ignores changes in fullscreen state initiated by the +// window manager (e.g. via a window manager accelerator key). +TEST_F(DesktopWindowTreeHostX11Test, WindowManagerTogglesFullscreen) { + if (!ui::WmSupportsHint(ui::GetAtom("_NET_WM_STATE_FULLSCREEN"))) + return; + + scoped_ptr<Widget> widget = CreateWidget(new ShapedWidgetDelegate()); + XID xid = widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget(); + widget->Show(); + ui::X11EventSource::GetInstance()->DispatchXEvents(); + + gfx::Rect initial_bounds = widget->GetWindowBoundsInScreen(); + { + WMStateWaiter waiter(xid, "_NET_WM_STATE_FULLSCREEN", true); + widget->SetFullscreen(true); + waiter.Wait(); + } + EXPECT_TRUE(widget->IsFullscreen()); + + // Emulate the window manager exiting fullscreen via a window manager + // accelerator key. It should not affect the widget's fullscreen state. + { + const char* kAtomsToCache[] = { + "_NET_WM_STATE", + "_NET_WM_STATE_FULLSCREEN", + NULL + }; + Display* display = gfx::GetXDisplay(); + ui::X11AtomCache atom_cache(display, kAtomsToCache); + + XEvent xclient; + memset(&xclient, 0, sizeof(xclient)); + xclient.type = ClientMessage; + xclient.xclient.window = xid; + xclient.xclient.message_type = atom_cache.GetAtom("_NET_WM_STATE"); + xclient.xclient.format = 32; + xclient.xclient.data.l[0] = 0; + xclient.xclient.data.l[1] = atom_cache.GetAtom("_NET_WM_STATE_FULLSCREEN"); + xclient.xclient.data.l[2] = 0; + xclient.xclient.data.l[3] = 1; + xclient.xclient.data.l[4] = 0; + XSendEvent(display, DefaultRootWindow(display), False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xclient); + + WMStateWaiter waiter(xid, "_NET_WM_STATE_FULLSCREEN", false); + waiter.Wait(); + } + EXPECT_TRUE(widget->IsFullscreen()); + + // Calling Widget::SetFullscreen(false) should clear the widget's fullscreen + // state and clean things up. + widget->SetFullscreen(false); + EXPECT_FALSE(widget->IsFullscreen()); + EXPECT_EQ(initial_bounds.ToString(), + widget->GetWindowBoundsInScreen().ToString()); +} + +// Tests that the minimization information is propagated to the content window. +TEST_F(DesktopWindowTreeHostX11Test, ToggleMinimizePropogateToContentWindow) { + Widget widget; + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.native_widget = new DesktopNativeWidgetAura(&widget); + widget.Init(params); + widget.Show(); + ui::X11EventSource::GetInstance()->DispatchXEvents(); + + XID xid = widget.GetNativeWindow()->GetHost()->GetAcceleratedWidget(); + Display* display = gfx::GetXDisplay(); + + // Minimize by sending _NET_WM_STATE_HIDDEN + { + const char* kAtomsToCache[] = { + "_NET_WM_STATE", + "_NET_WM_STATE_HIDDEN", + NULL + }; + + ui::X11AtomCache atom_cache(display, kAtomsToCache); + + std::vector< ::Atom> atom_list; + atom_list.push_back(atom_cache.GetAtom("_NET_WM_STATE_HIDDEN")); + ui::SetAtomArrayProperty(xid, "_NET_WM_STATE", "ATOM", atom_list); + + XEvent xevent; + memset(&xevent, 0, sizeof(xevent)); + xevent.type = PropertyNotify; + xevent.xproperty.type = PropertyNotify; + xevent.xproperty.send_event = 1; + xevent.xproperty.display = display; + xevent.xproperty.window = xid; + xevent.xproperty.atom = atom_cache.GetAtom("_NET_WM_STATE"); + xevent.xproperty.state = 0; + XSendEvent(display, DefaultRootWindow(display), False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xevent); + + WMStateWaiter waiter(xid, "_NET_WM_STATE_HIDDEN", true); + waiter.Wait(); + } + EXPECT_FALSE(widget.GetNativeWindow()->IsVisible()); + + // Show from minimized by sending _NET_WM_STATE_FOCUSED + { + const char* kAtomsToCache[] = { + "_NET_WM_STATE", + "_NET_WM_STATE_FOCUSED", + NULL + }; + + ui::X11AtomCache atom_cache(display, kAtomsToCache); + + std::vector< ::Atom> atom_list; + atom_list.push_back(atom_cache.GetAtom("_NET_WM_STATE_FOCUSED")); + ui::SetAtomArrayProperty(xid, "_NET_WM_STATE", "ATOM", atom_list); + + XEvent xevent; + memset(&xevent, 0, sizeof(xevent)); + xevent.type = PropertyNotify; + xevent.xproperty.type = PropertyNotify; + xevent.xproperty.send_event = 1; + xevent.xproperty.display = display; + xevent.xproperty.window = xid; + xevent.xproperty.atom = atom_cache.GetAtom("_NET_WM_STATE"); + xevent.xproperty.state = 0; + XSendEvent(display, DefaultRootWindow(display), False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xevent); + + WMStateWaiter waiter(xid, "_NET_WM_STATE_FOCUSED", true); + waiter.Wait(); + } + EXPECT_TRUE(widget.GetNativeWindow()->IsVisible()); +} + +} // 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 index 0cfe040cbdf..d43b853093b 100644 --- a/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.cc +++ b/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.cc @@ -9,19 +9,18 @@ #include "base/message_loop/message_loop.h" #include "ui/aura/env.h" -#include "ui/aura/root_window.h" +#include "ui/aura/window_event_dispatcher.h" +#include "ui/base/x/x11_menu_list.h" #include "ui/base/x/x11_util.h" - -#if !defined(OS_CHROMEOS) +#include "ui/events/platform/platform_event_source.h" +#include "ui/gfx/x/x11_error_tracker.h" #include "ui/views/ime/input_method.h" -#include "ui/views/widget/desktop_aura/desktop_root_window_host_x11.h" -#endif +#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" namespace { const char* kAtomsToCache[] = { "_NET_ACTIVE_WINDOW", - "_NET_SUPPORTED", NULL }; @@ -43,10 +42,12 @@ X11DesktopHandler* X11DesktopHandler::get() { X11DesktopHandler::X11DesktopHandler() : xdisplay_(gfx::GetXDisplay()), x_root_window_(DefaultRootWindow(xdisplay_)), + wm_user_time_ms_(0), current_window_(None), atom_cache_(xdisplay_, kAtomsToCache), wm_supports_active_window_(false) { - base::MessagePumpX11::Current()->AddDispatcherForRootWindow(this); + if (ui::PlatformEventSource::GetInstance()) + ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); aura::Env::GetInstance()->AddObserver(this); XWindowAttributes attr; @@ -55,22 +56,16 @@ X11DesktopHandler::X11DesktopHandler() attr.your_event_mask | PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask); - std::vector<Atom> atoms; - if (ui::GetAtomArrayProperty(x_root_window_, "_NET_ACTIVE_WINDOW", &atoms)) { - Atom active_window = atom_cache_.GetAtom("_NET_ACTIVE_WINDOW"); - for (std::vector<Atom>::iterator iter = atoms.begin(); iter != atoms.end(); - ++iter) { - if (*(iter) == active_window) { - wm_supports_active_window_ = true; - break; - } - } - } + ::Window active_window; + wm_supports_active_window_ = + ui::GetXIDProperty(x_root_window_, "_NET_ACTIVE_WINDOW", &active_window) && + active_window; } X11DesktopHandler::~X11DesktopHandler() { aura::Env::GetInstance()->RemoveObserver(this); - base::MessagePumpX11::Current()->RemoveDispatcherForRootWindow(this); + if (ui::PlatformEventSource::GetInstance()) + ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); } void X11DesktopHandler::ActivateWindow(::Window window) { @@ -84,7 +79,7 @@ void X11DesktopHandler::ActivateWindow(::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[1] = wm_user_time_ms_; xclient.xclient.data.l[2] = None; xclient.xclient.data.l[3] = 0; xclient.xclient.data.l[4] = 0; @@ -94,6 +89,12 @@ void X11DesktopHandler::ActivateWindow(::Window window) { &xclient); } else { XRaiseWindow(xdisplay_, window); + + // XRaiseWindow will not give input focus to the window. We now need to ask + // the X server to do that. Note that the call will raise an X error if the + // window is not mapped. + XSetInputFocus(xdisplay_, window, RevertToParent, CurrentTime); + OnActiveWindowChanged(window); } } @@ -102,16 +103,14 @@ bool X11DesktopHandler::IsActiveWindow(::Window window) const { return window == current_window_; } -void X11DesktopHandler::ProcessXEvent(const base::NativeEvent& event) { +void X11DesktopHandler::ProcessXEvent(XEvent* event) { switch (event->type) { - case EnterNotify: - if (event->xcrossing.focus == True && - current_window_ != event->xcrossing.window) - OnActiveWindowChanged(event->xcrossing.window); + case FocusIn: + if (current_window_ != event->xfocus.window) + OnActiveWindowChanged(event->xfocus.window); break; - case LeaveNotify: - if (event->xcrossing.focus == False && - current_window_ == event->xcrossing.window) + case FocusOut: + if (current_window_ == event->xfocus.window) OnActiveWindowChanged(None); break; default: @@ -119,25 +118,53 @@ void X11DesktopHandler::ProcessXEvent(const base::NativeEvent& event) { } } -bool X11DesktopHandler::Dispatch(const base::NativeEvent& event) { - // Check for a change to the active window. +bool X11DesktopHandler::CanDispatchEvent(const ui::PlatformEvent& event) { + return event->type == CreateNotify || event->type == DestroyNotify || + (event->type == PropertyNotify && + event->xproperty.window == x_root_window_); +} + +uint32_t X11DesktopHandler::DispatchEvent(const ui::PlatformEvent& event) { 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) && + // Check for a change to the active window. + CHECK_EQ(x_root_window_, event->xproperty.window); + ::Atom active_window_atom = atom_cache_.GetAtom("_NET_ACTIVE_WINDOW"); + if (event->xproperty.atom == active_window_atom) { + ::Window window; + if (ui::GetXIDProperty(x_root_window_, "_NET_ACTIVE_WINDOW", &window) && window) { - OnActiveWindowChanged(static_cast< ::Window>(window)); + OnActiveWindowChanged(window); } } break; } + + // Menus created by Chrome can be drag and drop targets. Since they are + // direct children of the screen root window and have override_redirect + // we cannot use regular _NET_CLIENT_LIST_STACKING property to find them + // and use a separate cache to keep track of them. + // TODO(varkha): Implement caching of all top level X windows and their + // coordinates and stacking order to eliminate repeated calls to X server + // during mouse movement, drag and shaping events. + case CreateNotify: { + // The window might be destroyed if the message pump haven't gotten a + // chance to run but we can safely ignore the X error. + gfx::X11ErrorTracker error_tracker; + XCreateWindowEvent *xcwe = &event->xcreatewindow; + ui::XMenuList::GetInstance()->MaybeRegisterMenu(xcwe->window); + break; + } + case DestroyNotify: { + XDestroyWindowEvent *xdwe = &event->xdestroywindow; + ui::XMenuList::GetInstance()->MaybeUnregisterMenu(xdwe->window); + break; + } + default: + NOTREACHED(); } - return true; + return ui::POST_DISPATCH_NONE; } void X11DesktopHandler::OnWindowInitialized(aura::Window* window) { @@ -151,17 +178,18 @@ void X11DesktopHandler::OnWillDestroyEnv() { void X11DesktopHandler::OnActiveWindowChanged(::Window xid) { if (current_window_ == xid) return; - DesktopRootWindowHostX11* old_host = - views::DesktopRootWindowHostX11::GetHostForXID(current_window_); + DesktopWindowTreeHostX11* old_host = + views::DesktopWindowTreeHostX11::GetHostForXID(current_window_); if (old_host) old_host->HandleNativeWidgetActivationChanged(false); - DesktopRootWindowHostX11* new_host = - views::DesktopRootWindowHostX11::GetHostForXID(xid); + // Update the current window ID to effectively change the active widget. + current_window_ = xid; + + DesktopWindowTreeHostX11* new_host = + views::DesktopWindowTreeHostX11::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 index 3399b401cf1..56b2084e7a5 100644 --- a/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.h +++ b/chromium/ui/views/widget/desktop_aura/x11_desktop_handler.h @@ -9,8 +9,10 @@ // 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 <vector> + #include "ui/aura/env_observer.h" +#include "ui/events/platform/platform_event_dispatcher.h" #include "ui/gfx/x/x11_atom_cache.h" #include "ui/gfx/x/x11_types.h" #include "ui/views/views_export.h" @@ -22,13 +24,23 @@ 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, +class VIEWS_EXPORT X11DesktopHandler : public ui::PlatformEventDispatcher, public aura::EnvObserver { public: // Returns the singleton handler. static X11DesktopHandler* get(); + // Gets/sets the X11 server time of the most recent mouse click, touch or + // key press on a Chrome window. + int wm_user_time_ms() const { + return wm_user_time_ms_; + } + void set_wm_user_time_ms(unsigned long time_ms) { + wm_user_time_ms_ = time_ms; + } + // Sends a request to the window manager to activate |window|. + // This method should only be called if the window is already mapped. void ActivateWindow(::Window window); // Checks if the current active window is |window|. @@ -37,10 +49,11 @@ class VIEWS_EXPORT X11DesktopHandler : public base::MessageLoop::Dispatcher, // Processes activation/focus related events. Some of these events are // dispatched to the X11 window dispatcher, and not to the X11 root-window // dispatcher. The window dispatcher sends these events to here. - void ProcessXEvent(const base::NativeEvent& event); + void ProcessXEvent(XEvent* event); - // Overridden from MessageLoop::Dispatcher: - virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE; + // ui::PlatformEventDispatcher + virtual bool CanDispatchEvent(const ui::PlatformEvent& event) OVERRIDE; + virtual uint32_t DispatchEvent(const ui::PlatformEvent& event) OVERRIDE; // Overridden from aura::EnvObserver: virtual void OnWindowInitialized(aura::Window* window) OVERRIDE; @@ -59,6 +72,10 @@ class VIEWS_EXPORT X11DesktopHandler : public base::MessageLoop::Dispatcher, // The native root window. ::Window x_root_window_; + // The X11 server time of the most recent mouse click, touch, or key press + // on a Chrome window. + unsigned long wm_user_time_ms_; + // The activated window. ::Window current_window_; 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 index a79d365ebc7..195d102a587 100644 --- 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 @@ -5,45 +5,22 @@ #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_x11.h" #include "base/run_loop.h" #include "ui/aura/env.h" -#include "ui/aura/root_window.h" #include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" #include "ui/base/x/x11_util.h" #include "ui/events/event.h" #include "ui/gfx/screen.h" -namespace { - -// Delay moving the window. -// -// When we receive a mouse move event, we have to have it processed in a -// different run through the message pump because moving the window will -// otherwise prevent tasks from running. -// -// This constant was derived with playing with builds of chrome; it has no -// theoretical justification. -// -// TODO(erg): This helps with the performance of dragging windows, but it -// doesn't really solve the hard problems, which is that various calls to X11, -// such as XQueryPointer, ui::IsWindowVisible() and ui::WindowContainsPoint() -// take a while to get replies and block in the process. I've seen all of the -// above take as long as 20ms to respond. -const int kMoveDelay = 3; - -} // namespace - namespace views { X11DesktopWindowMoveClient::X11DesktopWindowMoveClient() : move_loop_(this), - root_window_(NULL) { + host_(NULL) { } X11DesktopWindowMoveClient::~X11DesktopWindowMoveClient() {} @@ -51,15 +28,7 @@ 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_; - - gfx::Rect target_rect(system_loc, root_window_->host()->GetBounds().size()); - - window_move_timer_.Start( - FROM_HERE, - base::TimeDelta::FromMilliseconds(kMoveDelay), - base::Bind(&X11DesktopWindowMoveClient::SetHostBounds, - base::Unretained(this), - target_rect)); + host_->SetBounds(gfx::Rect(system_loc, host_->GetBounds().size())); } void X11DesktopWindowMoveClient::OnMouseReleased() { @@ -67,33 +36,25 @@ void X11DesktopWindowMoveClient::OnMouseReleased() { } void X11DesktopWindowMoveClient::OnMoveLoopEnded() { - root_window_ = NULL; + host_ = NULL; } //////////////////////////////////////////////////////////////////////////////// -// DesktopRootWindowHostLinux, aura::client::WindowMoveClient implementation: +// DesktopWindowTreeHostLinux, 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->GetDispatcher(); + host_ = source->GetHost(); - bool success = move_loop_.RunMoveLoop(source, root_window_->last_cursor()); + bool success = move_loop_.RunMoveLoop(source, host_->last_cursor()); return success ? aura::client::MOVE_SUCCESSFUL : aura::client::MOVE_CANCELED; } void X11DesktopWindowMoveClient::EndMoveLoop() { - window_move_timer_.Stop(); move_loop_.EndMoveLoop(); } -//////////////////////////////////////////////////////////////////////////////// -// DesktopRootWindowHostLinux, private: - -void X11DesktopWindowMoveClient::SetHostBounds(const gfx::Rect& rect) { - root_window_->SetHostBounds(rect); -} - } // 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 index 3be6a63c325..44bdd91ff70 100644 --- 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 @@ -7,25 +7,17 @@ #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 "base/timer/timer.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" +#include "ui/wm/public/window_move_client.h" namespace aura { -class RootWindow; -} - -namespace gfx { -class Rect; +class WindowTreeHost; } namespace views { @@ -51,21 +43,16 @@ class VIEWS_EXPORT X11DesktopWindowMoveClient : virtual void EndMoveLoop() OVERRIDE; private: - // Callback from |window_move_timer_|. - void SetHostBounds(const gfx::Rect& rect); - 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_; + aura::WindowTreeHost* host_; // 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_; - - base::OneShotTimer<X11DesktopWindowMoveClient> window_move_timer_; }; } // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/x11_scoped_capture.cc b/chromium/ui/views/widget/desktop_aura/x11_scoped_capture.cc new file mode 100644 index 00000000000..b48bb5005ba --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/x11_scoped_capture.cc @@ -0,0 +1,31 @@ +// 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/desktop_aura/x11_scoped_capture.h" + +#include <X11/X.h> +#include <X11/Xlib.h> + +#include "ui/gfx/x/x11_types.h" + +namespace views { + +X11ScopedCapture::X11ScopedCapture(XID window) + : captured_(false) { + // TODO(sad): Use XI2 API instead. + unsigned int event_mask = PointerMotionMask | ButtonReleaseMask | + ButtonPressMask; + int status = XGrabPointer(gfx::GetXDisplay(), window, True, event_mask, + GrabModeAsync, GrabModeAsync, None, None, + CurrentTime); + captured_ = status == GrabSuccess; +} + +X11ScopedCapture::~X11ScopedCapture() { + if (captured_) { + XUngrabPointer(gfx::GetXDisplay(), CurrentTime); + } +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/x11_scoped_capture.h b/chromium/ui/views/widget/desktop_aura/x11_scoped_capture.h new file mode 100644 index 00000000000..cd744c86451 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/x11_scoped_capture.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_X11_SCOPED_CAPTURE_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_X11_SCOPED_CAPTURE_H_ + +#include "base/basictypes.h" + +typedef unsigned long XID; + +namespace views { + +// Grabs X11 input on a window., i.e. all subsequent mouse events are dispatched +// to the window, while the capture is active. +class X11ScopedCapture { + public: + explicit X11ScopedCapture(XID window); + virtual ~X11ScopedCapture(); + + private: + bool captured_; + + DISALLOW_COPY_AND_ASSIGN(X11ScopedCapture); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_X11_SCOPED_CAPTURE_H_ diff --git a/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder.cc b/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder.cc new file mode 100644 index 00000000000..d59b46cf379 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder.cc @@ -0,0 +1,97 @@ +// Copyright 2014 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_topmost_window_finder.h" + +#include <X11/Xutil.h> + +#include "ui/aura/client/screen_position_client.h" +#include "ui/aura/window.h" +#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" + +namespace views { + +X11TopmostWindowFinder::X11TopmostWindowFinder() : toplevel_(None) { +} + +X11TopmostWindowFinder::~X11TopmostWindowFinder() { +} + +aura::Window* X11TopmostWindowFinder::FindLocalProcessWindowAt( + const gfx::Point& screen_loc, + const std::set<aura::Window*>& ignore) { + screen_loc_ = screen_loc; + ignore_ = ignore; + + std::vector<aura::Window*> local_process_windows = + DesktopWindowTreeHostX11::GetAllOpenWindows(); + bool found_local_process_window = false; + for (size_t i = 0; i < local_process_windows.size(); ++i) { + if (ShouldStopIteratingAtLocalProcessWindow(local_process_windows[i])) { + found_local_process_window = true; + break; + } + } + if (!found_local_process_window) + return NULL; + + ui::EnumerateTopLevelWindows(this); + return DesktopWindowTreeHostX11::GetContentWindowForXID(toplevel_); +} + +XID X11TopmostWindowFinder::FindWindowAt(const gfx::Point& screen_loc) { + screen_loc_ = screen_loc; + ui::EnumerateTopLevelWindows(this); + return toplevel_; +} + +bool X11TopmostWindowFinder::ShouldStopIterating(XID xid) { + if (!ui::IsWindowVisible(xid)) + return false; + + aura::Window* window = + views::DesktopWindowTreeHostX11::GetContentWindowForXID(xid); + if (window) { + if (ShouldStopIteratingAtLocalProcessWindow(window)) { + toplevel_ = xid; + return true; + } + return false; + } + + if (ui::WindowContainsPoint(xid, screen_loc_)) { + toplevel_ = xid; + return true; + } + return false; +} + +bool X11TopmostWindowFinder::ShouldStopIteratingAtLocalProcessWindow( + aura::Window* window) { + if (ignore_.find(window) != ignore_.end()) + return false; + + // Currently |window|->IsVisible() always returns true. + // TODO(pkotwicz): Fix this. crbug.com/353038 + if (!window->IsVisible()) + return false; + + DesktopWindowTreeHostX11* host = + DesktopWindowTreeHostX11::GetHostForXID( + window->GetHost()->GetAcceleratedWidget()); + if (!host->GetX11RootWindowOuterBounds().Contains(screen_loc_)) + return false; + + ::Region shape = host->GetWindowShape(); + if (!shape) + return true; + + aura::client::ScreenPositionClient* screen_position_client = + aura::client::GetScreenPositionClient(window->GetRootWindow()); + gfx::Point window_loc(screen_loc_); + screen_position_client->ConvertPointFromScreen(window, &window_loc); + return XPointInRegion(shape, window_loc.x(), window_loc.y()) == True; +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder.h b/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder.h new file mode 100644 index 00000000000..cf8498abe50 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder.h @@ -0,0 +1,53 @@ +// Copyright 2014 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_TOPMOST_WINDOW_FINDER_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_X11_TOPMOST_WINDOW_FINDER_H_ + +#include <set> + +#include "ui/base/x/x11_util.h" +#include "ui/gfx/point.h" +#include "ui/views/views_export.h" + +namespace aura { +class Window; +} + +namespace views { + +// Utility class for finding the topmost window at a given screen position. +class VIEWS_EXPORT X11TopmostWindowFinder + : public ui::EnumerateWindowsDelegate { + public: + X11TopmostWindowFinder(); + virtual ~X11TopmostWindowFinder(); + + // Returns the topmost window at |screen_loc|, ignoring the windows in + // |ignore|. Returns NULL if the topmost window at |screen_loc| does not + // belong to Chrome. + aura::Window* FindLocalProcessWindowAt(const gfx::Point& screen_loc, + const std::set<aura::Window*>& ignore); + + // Returns the topmost window at |screen_loc|. + XID FindWindowAt(const gfx::Point& screen_loc); + + private: + // ui::EnumerateWindowsDelegate: + virtual bool ShouldStopIterating(XID xid) OVERRIDE; + + // Returns true if |window| does not not belong to |ignore|, is visible and + // contains |screen_loc_|. + bool ShouldStopIteratingAtLocalProcessWindow(aura::Window* window); + + gfx::Point screen_loc_; + std::set<aura::Window*> ignore_; + XID toplevel_; + + DISALLOW_COPY_AND_ASSIGN(X11TopmostWindowFinder); +}; + +} // namespace + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_X11_TOPMOST_WINDOW_FINDER_H_ diff --git a/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_unittest.cc b/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_unittest.cc new file mode 100644 index 00000000000..ea9544528f8 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_unittest.cc @@ -0,0 +1,389 @@ +// Copyright 2014 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_topmost_window_finder.h" + +#include <algorithm> +#include <vector> +#include <X11/extensions/shape.h> +#include <X11/Xlib.h> +#include <X11/Xregion.h> + +// Get rid of X11 macros which conflict with gtest. +#undef Bool +#undef None + +#include "base/memory/scoped_ptr.h" +#include "third_party/skia/include/core/SkRect.h" +#include "third_party/skia/include/core/SkRegion.h" +#include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" +#include "ui/events/platform/x11/x11_event_source.h" +#include "ui/gfx/path.h" +#include "ui/gfx/path_x11.h" +#include "ui/gfx/x/x11_atom_cache.h" +#include "ui/views/test/views_test_base.h" +#include "ui/views/test/x11_property_change_waiter.h" +#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" +#include "ui/views/widget/desktop_aura/x11_desktop_handler.h" +#include "ui/views/widget/widget.h" + +namespace views { + +namespace { + +// Waits till |window| is minimized. +class MinimizeWaiter : public X11PropertyChangeWaiter { + public: + explicit MinimizeWaiter(XID window) + : X11PropertyChangeWaiter(window, "_NET_WM_STATE") { + const char* kAtomsToCache[] = { "_NET_WM_STATE_HIDDEN", NULL }; + atom_cache_.reset(new ui::X11AtomCache(gfx::GetXDisplay(), kAtomsToCache)); + } + + virtual ~MinimizeWaiter() { + } + + private: + // X11PropertyChangeWaiter: + virtual bool ShouldKeepOnWaiting(const ui::PlatformEvent& event) OVERRIDE { + std::vector<Atom> wm_states; + if (ui::GetAtomArrayProperty(xwindow(), "_NET_WM_STATE", &wm_states)) { + std::vector<Atom>::iterator it = std::find( + wm_states.begin(), + wm_states.end(), + atom_cache_->GetAtom("_NET_WM_STATE_HIDDEN")); + return it == wm_states.end(); + } + return true; + } + + scoped_ptr<ui::X11AtomCache> atom_cache_; + + DISALLOW_COPY_AND_ASSIGN(MinimizeWaiter); +}; + +// Waits till |_NET_CLIENT_LIST_STACKING| is updated to include +// |expected_windows|. +class StackingClientListWaiter : public X11PropertyChangeWaiter { + public: + StackingClientListWaiter(XID* expected_windows, size_t count) + : X11PropertyChangeWaiter(ui::GetX11RootWindow(), + "_NET_CLIENT_LIST_STACKING"), + expected_windows_(expected_windows, expected_windows + count) { + } + + virtual ~StackingClientListWaiter() { + } + + // X11PropertyChangeWaiter: + virtual void Wait() OVERRIDE { + // StackingClientListWaiter may be created after + // _NET_CLIENT_LIST_STACKING already contains |expected_windows|. + if (!ShouldKeepOnWaiting(NULL)) + return; + + X11PropertyChangeWaiter::Wait(); + } + + private: + // X11PropertyChangeWaiter: + virtual bool ShouldKeepOnWaiting(const ui::PlatformEvent& event) OVERRIDE { + std::vector<XID> stack; + ui::GetXWindowStack(ui::GetX11RootWindow(), &stack); + for (size_t i = 0; i < expected_windows_.size(); ++i) { + std::vector<XID>::iterator it = std::find( + stack.begin(), stack.end(), expected_windows_[i]); + if (it == stack.end()) + return true; + } + return false; + } + + std::vector<XID> expected_windows_; + + DISALLOW_COPY_AND_ASSIGN(StackingClientListWaiter); +}; + +} // namespace + +class X11TopmostWindowFinderTest : public ViewsTestBase { + public: + X11TopmostWindowFinderTest() { + } + + virtual ~X11TopmostWindowFinderTest() { + } + + // Creates and shows a Widget with |bounds|. The caller takes ownership of + // the returned widget. + scoped_ptr<Widget> CreateAndShowWidget(const gfx::Rect& bounds) { + scoped_ptr<Widget> toplevel(new Widget); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.native_widget = new DesktopNativeWidgetAura(toplevel.get()); + params.bounds = bounds; + params.remove_standard_frame = true; + toplevel->Init(params); + toplevel->Show(); + return toplevel.Pass(); + } + + // Creates and shows an X window with |bounds|. + XID CreateAndShowXWindow(const gfx::Rect& bounds) { + XID root = DefaultRootWindow(xdisplay()); + XID xid = XCreateSimpleWindow(xdisplay(), + root, + 0, 0, 1, 1, + 0, // border_width + 0, // border + 0); // background + + ui::SetUseOSWindowFrame(xid, false); + ShowAndSetXWindowBounds(xid, bounds); + return xid; + } + + // Shows |xid| and sets its bounds. + void ShowAndSetXWindowBounds(XID xid, const gfx::Rect& bounds) { + XMapWindow(xdisplay(), xid); + + XWindowChanges changes = {0}; + changes.x = bounds.x(); + changes.y = bounds.y(); + changes.width = bounds.width(); + changes.height = bounds.height(); + XConfigureWindow(xdisplay(), + xid, + CWX | CWY | CWWidth | CWHeight, + &changes); + } + + Display* xdisplay() { + return gfx::GetXDisplay(); + } + + // Returns the topmost X window at the passed in screen position. + XID FindTopmostXWindowAt(int screen_x, int screen_y) { + X11TopmostWindowFinder finder; + return finder.FindWindowAt(gfx::Point(screen_x, screen_y)); + } + + // Returns the topmost aura::Window at the passed in screen position. Returns + // NULL if the topmost window does not have an associated aura::Window. + aura::Window* FindTopmostLocalProcessWindowAt(int screen_x, int screen_y) { + X11TopmostWindowFinder finder; + return finder.FindLocalProcessWindowAt(gfx::Point(screen_x, screen_y), + std::set<aura::Window*>()); + } + + // Returns the topmost aura::Window at the passed in screen position ignoring + // |ignore_window|. Returns NULL if the topmost window does not have an + // associated aura::Window. + aura::Window* FindTopmostLocalProcessWindowWithIgnore( + int screen_x, + int screen_y, + aura::Window* ignore_window) { + std::set<aura::Window*> ignore; + ignore.insert(ignore_window); + X11TopmostWindowFinder finder; + return finder.FindLocalProcessWindowAt(gfx::Point(screen_x, screen_y), + ignore); + } + + // ViewsTestBase: + virtual void SetUp() OVERRIDE { + ViewsTestBase::SetUp(); + + // Make X11 synchronous for our display connection. This does not force the + // window manager to behave synchronously. + XSynchronize(xdisplay(), True); + + // Ensure that the X11DesktopHandler exists. The X11DesktopHandler is + // necessary to properly track menu windows. + X11DesktopHandler::get(); + } + + virtual void TearDown() OVERRIDE { + XSynchronize(xdisplay(), False); + ViewsTestBase::TearDown(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(X11TopmostWindowFinderTest); +}; + +TEST_F(X11TopmostWindowFinderTest, Basic) { + // Avoid positioning test windows at 0x0 because window managers often have a + // panel/launcher along one of the screen edges and do not allow windows to + // position themselves to overlap the panel/launcher. + scoped_ptr<Widget> widget1( + CreateAndShowWidget(gfx::Rect(100, 100, 200, 100))); + aura::Window* window1 = widget1->GetNativeWindow(); + XID xid1 = window1->GetHost()->GetAcceleratedWidget(); + + XID xid2 = CreateAndShowXWindow(gfx::Rect(200, 100, 100, 200)); + + scoped_ptr<Widget> widget3( + CreateAndShowWidget(gfx::Rect(100, 190, 200, 110))); + aura::Window* window3 = widget3->GetNativeWindow(); + XID xid3 = window3->GetHost()->GetAcceleratedWidget(); + + XID xids[] = { xid1, xid2, xid3 }; + StackingClientListWaiter waiter(xids, arraysize(xids)); + waiter.Wait(); + ui::X11EventSource::GetInstance()->DispatchXEvents(); + + EXPECT_EQ(xid1, FindTopmostXWindowAt(150, 150)); + EXPECT_EQ(window1, FindTopmostLocalProcessWindowAt(150, 150)); + + EXPECT_EQ(xid2, FindTopmostXWindowAt(250, 150)); + EXPECT_EQ(NULL, FindTopmostLocalProcessWindowAt(250, 150)); + + EXPECT_EQ(xid3, FindTopmostXWindowAt(250, 250)); + EXPECT_EQ(window3, FindTopmostLocalProcessWindowAt(250, 250)); + + EXPECT_EQ(xid3, FindTopmostXWindowAt(150, 250)); + EXPECT_EQ(window3, FindTopmostLocalProcessWindowAt(150, 250)); + + EXPECT_EQ(xid3, FindTopmostXWindowAt(150, 195)); + EXPECT_EQ(window3, FindTopmostLocalProcessWindowAt(150, 195)); + + EXPECT_NE(xid1, FindTopmostXWindowAt(1000, 1000)); + EXPECT_NE(xid2, FindTopmostXWindowAt(1000, 1000)); + EXPECT_NE(xid3, FindTopmostXWindowAt(1000, 1000)); + EXPECT_EQ(NULL, FindTopmostLocalProcessWindowAt(1000, 1000)); + + EXPECT_EQ(window1, + FindTopmostLocalProcessWindowWithIgnore(150, 150, window3)); + EXPECT_EQ(NULL, + FindTopmostLocalProcessWindowWithIgnore(250, 250, window3)); + EXPECT_EQ(NULL, + FindTopmostLocalProcessWindowWithIgnore(150, 250, window3)); + EXPECT_EQ(window1, + FindTopmostLocalProcessWindowWithIgnore(150, 195, window3)); + + XDestroyWindow(xdisplay(), xid2); +} + +// Test that the minimized state is properly handled. +TEST_F(X11TopmostWindowFinderTest, Minimized) { + scoped_ptr<Widget> widget1( + CreateAndShowWidget(gfx::Rect(100, 100, 100, 100))); + aura::Window* window1 = widget1->GetNativeWindow(); + XID xid1 = window1->GetHost()->GetAcceleratedWidget(); + XID xid2 = CreateAndShowXWindow(gfx::Rect(300, 100, 100, 100)); + + XID xids[] = { xid1, xid2 }; + StackingClientListWaiter stack_waiter(xids, arraysize(xids)); + stack_waiter.Wait(); + ui::X11EventSource::GetInstance()->DispatchXEvents(); + + EXPECT_EQ(xid1, FindTopmostXWindowAt(150, 150)); + { + MinimizeWaiter minimize_waiter(xid1); + XIconifyWindow(xdisplay(), xid1, 0); + minimize_waiter.Wait(); + } + EXPECT_NE(xid1, FindTopmostXWindowAt(150, 150)); + EXPECT_NE(xid2, FindTopmostXWindowAt(150, 150)); + + // Repeat test for an X window which does not belong to a views::Widget + // because the code path is different. + EXPECT_EQ(xid2, FindTopmostXWindowAt(350, 150)); + { + MinimizeWaiter minimize_waiter(xid2); + XIconifyWindow(xdisplay(), xid2, 0); + minimize_waiter.Wait(); + } + EXPECT_NE(xid1, FindTopmostXWindowAt(350, 150)); + EXPECT_NE(xid2, FindTopmostXWindowAt(350, 150)); + + XDestroyWindow(xdisplay(), xid2); +} + +// Test that non-rectangular windows are properly handled. +TEST_F(X11TopmostWindowFinderTest, NonRectangular) { + if (!ui::IsShapeExtensionAvailable()) + return; + + scoped_ptr<Widget> widget1( + CreateAndShowWidget(gfx::Rect(100, 100, 100, 100))); + XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget(); + SkRegion* skregion1 = new SkRegion; + skregion1->op(SkIRect::MakeXYWH(0, 10, 10, 90), SkRegion::kUnion_Op); + skregion1->op(SkIRect::MakeXYWH(10, 0, 90, 100), SkRegion::kUnion_Op); + // Widget takes ownership of |skregion1|. + widget1->SetShape(skregion1); + + SkRegion skregion2; + skregion2.op(SkIRect::MakeXYWH(0, 10, 10, 90), SkRegion::kUnion_Op); + skregion2.op(SkIRect::MakeXYWH(10, 0, 90, 100), SkRegion::kUnion_Op); + XID xid2 = CreateAndShowXWindow(gfx::Rect(300, 100, 100, 100)); + REGION* region2 = gfx::CreateRegionFromSkRegion(skregion2); + XShapeCombineRegion(xdisplay(), xid2, ShapeBounding, 0, 0, region2, + false); + XDestroyRegion(region2); + + XID xids[] = { xid1, xid2 }; + StackingClientListWaiter stack_waiter(xids, arraysize(xids)); + stack_waiter.Wait(); + ui::X11EventSource::GetInstance()->DispatchXEvents(); + + EXPECT_EQ(xid1, FindTopmostXWindowAt(105, 120)); + EXPECT_NE(xid1, FindTopmostXWindowAt(105, 105)); + EXPECT_NE(xid2, FindTopmostXWindowAt(105, 105)); + + // Repeat test for an X window which does not belong to a views::Widget + // because the code path is different. + EXPECT_EQ(xid2, FindTopmostXWindowAt(305, 120)); + EXPECT_NE(xid1, FindTopmostXWindowAt(305, 105)); + EXPECT_NE(xid2, FindTopmostXWindowAt(305, 105)); + + XDestroyWindow(xdisplay(), xid2); +} + +// Test that the TopmostWindowFinder finds windows which belong to menus +// (which may or may not belong to Chrome). +TEST_F(X11TopmostWindowFinderTest, Menu) { + XID xid = CreateAndShowXWindow(gfx::Rect(100, 100, 100, 100)); + + XID root = DefaultRootWindow(xdisplay()); + XSetWindowAttributes swa; + swa.override_redirect = True; + XID menu_xid = XCreateWindow(xdisplay(), + root, + 0, 0, 1, 1, + 0, // border width + CopyFromParent, // depth + InputOutput, + CopyFromParent, // visual + CWOverrideRedirect, + &swa); + { + const char* kAtomsToCache[] = { "_NET_WM_WINDOW_TYPE_MENU", NULL }; + ui::X11AtomCache atom_cache(gfx::GetXDisplay(), kAtomsToCache); + ui::SetAtomProperty(menu_xid, + "_NET_WM_WINDOW_TYPE", + "ATOM", + atom_cache.GetAtom("_NET_WM_WINDOW_TYPE_MENU")); + } + ui::SetUseOSWindowFrame(menu_xid, false); + ShowAndSetXWindowBounds(menu_xid, gfx::Rect(140, 110, 100, 100)); + ui::X11EventSource::GetInstance()->DispatchXEvents(); + + // |menu_xid| is never added to _NET_CLIENT_LIST_STACKING. + XID xids[] = { xid }; + StackingClientListWaiter stack_waiter(xids, arraysize(xids)); + stack_waiter.Wait(); + + EXPECT_EQ(xid, FindTopmostXWindowAt(110, 110)); + EXPECT_EQ(menu_xid, FindTopmostXWindowAt(150, 120)); + EXPECT_EQ(menu_xid, FindTopmostXWindowAt(210, 120)); + + XDestroyWindow(xdisplay(), xid); + XDestroyWindow(xdisplay(), menu_xid); +} + +} // namespace views 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 index 20c061bd670..6c0b3c6b9e7 100644 --- 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 @@ -8,16 +8,20 @@ // 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/bind.h" #include "base/message_loop/message_loop.h" -#include "base/message_loop/message_pump_x11.h" #include "base/run_loop.h" +#include "third_party/skia/include/core/SkBitmap.h" #include "ui/aura/env.h" -#include "ui/aura/root_window.h" #include "ui/aura/window.h" +#include "ui/aura/window_event_dispatcher.h" #include "ui/aura/window_tree_host.h" #include "ui/base/x/x11_util.h" #include "ui/events/event.h" +#include "ui/events/event_utils.h" +#include "ui/events/keycodes/keyboard_code_conversion_x.h" +#include "ui/events/platform/scoped_event_dispatcher.h" +#include "ui/events/platform/x11/x11_event_source.h" #include "ui/gfx/point_conversions.h" #include "ui/gfx/screen.h" #include "ui/views/controls/image_view.h" @@ -27,9 +31,14 @@ namespace views { namespace { +// The minimum alpha before we declare a pixel transparent when searching in +// our source image. +const uint32 kMinAlpha = 32; +const unsigned char kDragWidgetOpacity = 0xc0; + class ScopedCapturer { public: - explicit ScopedCapturer(aura::RootWindowHost* host) + explicit ScopedCapturer(aura::WindowTreeHost* host) : host_(host) { host_->SetCapture(); } @@ -39,7 +48,7 @@ class ScopedCapturer { } private: - aura::RootWindowHost* host_; + aura::WindowTreeHost* host_; DISALLOW_COPY_AND_ASSIGN(ScopedCapturer); }; @@ -50,15 +59,42 @@ X11WholeScreenMoveLoop::X11WholeScreenMoveLoop( X11WholeScreenMoveLoopDelegate* delegate) : delegate_(delegate), in_move_loop_(false), - grab_input_window_(None) { + should_reset_mouse_flags_(false), + grab_input_window_(None), + canceled_(false), + has_grab_(false), + weak_factory_(this) { + last_xmotion_.type = LASTEvent; } X11WholeScreenMoveLoop::~X11WholeScreenMoveLoop() {} +void X11WholeScreenMoveLoop::DispatchMouseMovement() { + if (!weak_factory_.HasWeakPtrs()) + return; + weak_factory_.InvalidateWeakPtrs(); + DCHECK_EQ(MotionNotify, last_xmotion_.type); + delegate_->OnMouseMovement(&last_xmotion_); + last_xmotion_.type = LASTEvent; +} + //////////////////////////////////////////////////////////////////////////////// -// DesktopRootWindowHostLinux, MessageLoop::Dispatcher implementation: +// DesktopWindowTreeHostLinux, ui::PlatformEventDispatcher implementation: + +bool X11WholeScreenMoveLoop::CanDispatchEvent(const ui::PlatformEvent& event) { + return in_move_loop_; +} -bool X11WholeScreenMoveLoop::Dispatch(const base::NativeEvent& event) { +uint32_t X11WholeScreenMoveLoop::DispatchEvent(const ui::PlatformEvent& event) { + // This method processes all events for the grab_input_window_ as well as + // mouse events for all windows while the move loop is active - even before + // the grab is granted by X. This allows mouse notification events that were + // sent after the capture was requested but before the capture was granted + // to be dispatched. It is especially important to process the mouse release + // event that should have stopped the drag even if that mouse release happened + // before the grab was granted. + if (!in_move_loop_) + return ui::POST_DISPATCH_PERFORM_DEFAULT; XEvent* xev = event; // Note: the escape key is handled in the tab drag controller, which has @@ -70,70 +106,152 @@ bool X11WholeScreenMoveLoop::Dispatch(const base::NativeEvent& event) { gfx::Point location = gfx::ToFlooredPoint( screen->GetCursorScreenPoint() - drag_offset_); drag_widget_->SetBounds(gfx::Rect(location, drag_image_.size())); + drag_widget_->StackAtTop(); } - delegate_->OnMouseMovement(&xev->xmotion); - break; + last_xmotion_ = xev->xmotion; + if (!weak_factory_.HasWeakPtrs()) { + // Post a task to dispatch mouse movement event when control returns to + // the message loop. This allows smoother dragging since the events are + // dispatched without waiting for the drag widget updates. + base::MessageLoopForUI::current()->PostTask( + FROM_HERE, + base::Bind(&X11WholeScreenMoveLoop::DispatchMouseMovement, + weak_factory_.GetWeakPtr())); + } + return ui::POST_DISPATCH_NONE; } 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. + DispatchMouseMovement(); delegate_->OnMouseReleased(); } + return ui::POST_DISPATCH_NONE; + } + case KeyPress: { + if (ui::KeyboardCodeFromXKeyEvent(xev) == ui::VKEY_ESCAPE) { + canceled_ = true; + EndMoveLoop(); + return ui::POST_DISPATCH_NONE; + } break; } + case FocusOut: { + if (xev->xfocus.mode != NotifyGrab) + has_grab_ = false; + break; + } + case GenericEvent: { + ui::EventType type = ui::EventTypeFromNative(xev); + switch (type) { + case ui::ET_MOUSE_MOVED: + case ui::ET_MOUSE_DRAGGED: + case ui::ET_MOUSE_RELEASED: { + XEvent xevent = {0}; + if (type == ui::ET_MOUSE_RELEASED) { + xevent.type = ButtonRelease; + xevent.xbutton.button = ui::EventButtonFromNative(xev); + } else { + xevent.type = MotionNotify; + } + xevent.xany.display = xev->xgeneric.display; + xevent.xany.window = grab_input_window_; + // 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 = DefaultRootWindow(xev->xgeneric.display); + xevent.xmotion.time = ui::EventTimeFromNative(xev).InMilliseconds(); + gfx::Point point(ui::EventSystemLocationFromNative(xev)); + xevent.xmotion.x_root = point.x(); + xevent.xmotion.y_root = point.y(); + DispatchEvent(&xevent); + return ui::POST_DISPATCH_NONE; + } + default: + break; + } + } } - return true; + return (event->xany.window == grab_input_window_) ? + ui::POST_DISPATCH_NONE : ui::POST_DISPATCH_PERFORM_DEFAULT; } -//////////////////////////////////////////////////////////////////////////////// -// DesktopRootWindowHostLinux, aura::client::WindowMoveClient implementation: - bool X11WholeScreenMoveLoop::RunMoveLoop(aura::Window* source, gfx::NativeCursor cursor) { - // Start a capture on the host, so that it continues to receive events during - // the drag. - ScopedCapturer capturer(source->GetDispatcher()->host()); - DCHECK(!in_move_loop_); // Can only handle one nested loop at a time. - in_move_loop_ = true; - XDisplay* display = gfx::GetXDisplay(); + // Start a capture on the host, so that it continues to receive events during + // the drag. This may be second time we are capturing the mouse events - the + // first being when a mouse is first pressed. That first capture needs to be + // released before the call to GrabPointerAndKeyboard below, otherwise it may + // get released while we still need the pointer grab, which is why we restrict + // the scope here. + { + ScopedCapturer capturer(source->GetHost()); + + grab_input_window_ = CreateDragInputWindow(gfx::GetXDisplay()); + // Releasing ScopedCapturer ensures that any other instance of + // X11ScopedCapture will not prematurely release grab that will be acquired + // below. + } + // TODO(varkha): Consider integrating GrabPointerAndKeyboard with + // ScopedCapturer to avoid possibility of logically keeping multiple grabs. + if (!GrabPointerAndKeyboard(cursor)) { + XDestroyWindow(gfx::GetXDisplay(), grab_input_window_); + return false; + } - grab_input_window_ = CreateDragInputWindow(display); - if (!drag_image_.isNull()) + scoped_ptr<ui::ScopedEventDispatcher> old_dispatcher = + nested_dispatcher_.Pass(); + nested_dispatcher_ = + ui::PlatformEventSource::GetInstance()->OverrideDispatcher(this); + if (!drag_image_.isNull() && CheckIfIconValid()) CreateDragImageWindow(); - base::MessagePumpX11::Current()->AddDispatcherForWindow( - this, grab_input_window_); - - if (!GrabPointerWithCursor(cursor)) - return false; // We are handling a mouse drag outside of the aura::RootWindow system. We // must manually make aura think that the mouse button is pressed so that we // don't draw extraneous tooltips. - aura::Env::GetInstance()->set_mouse_button_flags(ui::EF_LEFT_MOUSE_BUTTON); + aura::Env* env = aura::Env::GetInstance(); + if (!env->IsMouseButtonDown()) { + env->set_mouse_button_flags(ui::EF_LEFT_MOUSE_BUTTON); + should_reset_mouse_flags_ = true; + } + in_move_loop_ = true; + canceled_ = false; base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); base::MessageLoop::ScopedNestableTaskAllower allow_nested(loop); - base::RunLoop run_loop(aura::Env::GetInstance()->GetDispatcher()); + base::RunLoop run_loop; quit_closure_ = run_loop.QuitClosure(); run_loop.Run(); - return true; + nested_dispatcher_ = old_dispatcher.Pass(); + return !canceled_; } void X11WholeScreenMoveLoop::UpdateCursor(gfx::NativeCursor cursor) { - DCHECK(in_move_loop_); - GrabPointerWithCursor(cursor); + if (in_move_loop_) { + // If we're still in the move loop, regrab the pointer with the updated + // cursor. Note: we can be called from handling an XdndStatus message after + // EndMoveLoop() was called, but before we return from the nested RunLoop. + GrabPointerAndKeyboard(cursor); + } } void X11WholeScreenMoveLoop::EndMoveLoop() { if (!in_move_loop_) return; + // Prevent DispatchMouseMovement from dispatching any posted motion event. + weak_factory_.InvalidateWeakPtrs(); + last_xmotion_.type = LASTEvent; + // We undo our emulated mouse click from RunMoveLoop(); - aura::Env::GetInstance()->set_mouse_button_flags(0); + if (should_reset_mouse_flags_) { + aura::Env::GetInstance()->set_mouse_button_flags(0); + should_reset_mouse_flags_ = false; + } // 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 @@ -141,13 +259,18 @@ void X11WholeScreenMoveLoop::EndMoveLoop() { // Ungrab before we let go of the window. XDisplay* display = gfx::GetXDisplay(); - XUngrabPointer(display, CurrentTime); + // Only ungrab pointer if capture was not switched to another window. + if (has_grab_) { + XUngrabPointer(display, CurrentTime); + XUngrabKeyboard(display, CurrentTime); + } - base::MessagePumpX11::Current()->RemoveDispatcherForWindow( - grab_input_window_); + // Restore the previous dispatcher. + nested_dispatcher_.reset(); drag_widget_.reset(); delegate_->OnMoveLoopEnded(); XDestroyWindow(display, grab_input_window_); + grab_input_window_ = None; in_move_loop_ = false; quit_closure_.Run(); @@ -162,9 +285,10 @@ void X11WholeScreenMoveLoop::SetDragImage(const gfx::ImageSkia& image, drag_offset_.set_y(0.f); } -bool X11WholeScreenMoveLoop::GrabPointerWithCursor(gfx::NativeCursor cursor) { +bool X11WholeScreenMoveLoop::GrabPointerAndKeyboard(gfx::NativeCursor cursor) { XDisplay* display = gfx::GetXDisplay(); XGrabServer(display); + XUngrabPointer(display, CurrentTime); int ret = XGrabPointer( display, @@ -176,14 +300,28 @@ bool X11WholeScreenMoveLoop::GrabPointerWithCursor(gfx::NativeCursor cursor) { None, cursor.platform(), CurrentTime); - XUngrabServer(display); if (ret != GrabSuccess) { - DLOG(ERROR) << "Grabbing new tab for dragging failed: " + DLOG(ERROR) << "Grabbing pointer for dragging failed: " << ui::GetX11ErrorString(display, ret); - return false; + } else { + has_grab_ = true; + XUngrabKeyboard(display, CurrentTime); + ret = XGrabKeyboard( + display, + grab_input_window_, + False, + GrabModeAsync, + GrabModeAsync, + CurrentTime); + if (ret != GrabSuccess) { + DLOG(ERROR) << "Grabbing keyboard for dragging failed: " + << ui::GetX11ErrorString(display, ret); + } } - return true; + XUngrabServer(display); + XFlush(display); + return ret == GrabSuccess; } Window X11WholeScreenMoveLoop::CreateDragInputWindow(XDisplay* display) { @@ -196,7 +334,7 @@ Window X11WholeScreenMoveLoop::CreateDragInputWindow(XDisplay* display) { XSetWindowAttributes swa; memset(&swa, 0, sizeof(swa)); swa.event_mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | - StructureNotifyMask; + KeyPressMask | KeyReleaseMask | StructureNotifyMask; swa.override_redirect = True; Window window = XCreateWindow(display, DefaultRootWindow(display), @@ -204,7 +342,7 @@ Window X11WholeScreenMoveLoop::CreateDragInputWindow(XDisplay* display) { 0, CopyFromParent, InputOnly, CopyFromParent, attribute_mask, &swa); XMapRaised(display, window); - base::MessagePumpX11::Current()->BlockUntilWindowMapped(window); + ui::X11EventSource::GetInstance()->BlockUntilWindowMapped(window); return window; } @@ -221,17 +359,35 @@ void X11WholeScreenMoveLoop::CreateDragImageWindow() { widget->set_focus_on_creation(false); widget->set_frame_type(Widget::FRAME_TYPE_FORCE_NATIVE); widget->Init(params); + widget->SetOpacity(kDragWidgetOpacity); widget->GetNativeWindow()->SetName("DragWindow"); ImageView* image = new ImageView(); image->SetImage(drag_image_); image->SetBounds(0, 0, drag_image_.width(), drag_image_.height()); widget->SetContentsView(image); - widget->Show(); widget->GetNativeWindow()->layer()->SetFillsBoundsOpaquely(false); drag_widget_.reset(widget); } +bool X11WholeScreenMoveLoop::CheckIfIconValid() { + // Because we need a GL context per window, we do a quick check so that we + // don't make another context if the window would just be displaying a mostly + // transparent image. + const SkBitmap* in_bitmap = drag_image_.bitmap(); + SkAutoLockPixels in_lock(*in_bitmap); + for (int y = 0; y < in_bitmap->height(); ++y) { + uint32* in_row = in_bitmap->getAddr32(0, y); + + for (int x = 0; x < in_bitmap->width(); ++x) { + if (SkColorGetA(in_row[x]) > kMinAlpha) + return true; + } + } + + return false; +} + } // 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 index c0c25466647..cf3665ecac6 100644 --- 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 @@ -5,8 +5,10 @@ #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/callback.h" #include "base/compiler_specific.h" -#include "base/message_loop/message_loop.h" +#include "base/memory/weak_ptr.h" +#include "ui/events/platform/platform_event_dispatcher.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/vector2d_f.h" @@ -18,23 +20,29 @@ namespace aura { class Window; } +namespace ui { +class ScopedEventDispatcher; +} + namespace views { class Widget; // Runs a nested message loop and grabs the mouse. This is used to implement // dragging. -class X11WholeScreenMoveLoop : public base::MessageLoop::Dispatcher { +class X11WholeScreenMoveLoop : public ui::PlatformEventDispatcher { public: explicit X11WholeScreenMoveLoop(X11WholeScreenMoveLoopDelegate* delegate); virtual ~X11WholeScreenMoveLoop(); - // Overridden from base::MessageLoop::Dispatcher: - virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE; + // ui:::PlatformEventDispatcher: + virtual bool CanDispatchEvent(const ui::PlatformEvent& event) OVERRIDE; + virtual uint32_t DispatchEvent(const ui::PlatformEvent& 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. + // the mouse cursor. Returns true if the move-loop is completed successfully. + // If the pointer-grab fails, or the move-loop is canceled by the user (e.g. + // by pressing escape), then returns false. bool RunMoveLoop(aura::Window* window, gfx::NativeCursor cursor); // Updates the cursor while the move loop is running. @@ -47,9 +55,9 @@ class X11WholeScreenMoveLoop : public base::MessageLoop::Dispatcher { void SetDragImage(const gfx::ImageSkia& image, gfx::Vector2dF offset); private: - // Grabs the pointer, setting the mouse cursor to |cursor|. Returns true if - // the grab was successful. - bool GrabPointerWithCursor(gfx::NativeCursor cursor); + // Grabs the pointer and keyboard, setting the mouse cursor to |cursor|. + // Returns true if the grab was successful. + bool GrabPointerAndKeyboard(gfx::NativeCursor cursor); // Creates an input-only window to be used during the drag. Window CreateDragInputWindow(XDisplay* display); @@ -57,10 +65,20 @@ class X11WholeScreenMoveLoop : public base::MessageLoop::Dispatcher { // Creates a window to show the drag image during the drag. void CreateDragImageWindow(); + // Checks to see if |in_image| is an image that has any visible regions + // (defined as having a pixel with alpha > 32). If so, return true. + bool CheckIfIconValid(); + + // Dispatch mouse movement event to |delegate_| in a posted task. + void DispatchMouseMovement(); + X11WholeScreenMoveLoopDelegate* delegate_; // Are we running a nested message loop from RunMoveLoop()? bool in_move_loop_; + scoped_ptr<ui::ScopedEventDispatcher> nested_dispatcher_; + + bool should_reset_mouse_flags_; // 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 @@ -69,11 +87,20 @@ class X11WholeScreenMoveLoop : public base::MessageLoop::Dispatcher { base::Closure quit_closure_; + // Keeps track of whether the move-loop is cancled by the user (e.g. by + // pressing escape). + bool canceled_; + + // Keeps track of whether we still have a pointer grab at the end of the loop. + bool has_grab_; + // A Widget is created during the drag if there is an image available to be // used during the drag. scoped_ptr<Widget> drag_widget_; gfx::ImageSkia drag_image_; gfx::Vector2dF drag_offset_; + XMotionEvent last_xmotion_; + base::WeakPtrFactory<X11WholeScreenMoveLoop> weak_factory_; DISALLOW_COPY_AND_ASSIGN(X11WholeScreenMoveLoop); }; 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 index 3f415f0050d..ab992c66806 100644 --- a/chromium/ui/views/widget/desktop_aura/x11_window_event_filter.cc +++ b/chromium/ui/views/widget/desktop_aura/x11_window_event_filter.cc @@ -9,15 +9,20 @@ #include <X11/Xatom.h> #include <X11/Xlib.h> -#include "ui/aura/root_window.h" +#include "ui/aura/client/aura_constants.h" #include "ui/aura/window.h" #include "ui/aura/window_delegate.h" +#include "ui/aura/window_tree_host.h" #include "ui/base/hit_test.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" +#include "ui/gfx/display.h" +#include "ui/gfx/screen.h" #include "ui/gfx/x/x11_types.h" -#include "ui/views/widget/desktop_aura/desktop_root_window_host.h" +#include "ui/views/linux_ui/linux_ui.h" +#include "ui/views/widget/desktop_aura/desktop_window_tree_host.h" #include "ui/views/widget/native_widget_aura.h" +#include "ui/views/widget/widget.h" namespace { @@ -33,24 +38,7 @@ 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 }; @@ -60,70 +48,134 @@ const char* kAtomsToCache[] = { namespace views { X11WindowEventFilter::X11WindowEventFilter( - aura::RootWindow* root_window, - DesktopRootWindowHost* root_window_host) + DesktopWindowTreeHost* window_tree_host) : xdisplay_(gfx::GetXDisplay()), - xwindow_(root_window->host()->GetAcceleratedWidget()), + xwindow_(window_tree_host->AsWindowTreeHost()->GetAcceleratedWidget()), x_root_window_(DefaultRootWindow(xdisplay_)), atom_cache_(xdisplay_, kAtomsToCache), - root_window_host_(root_window_host), - is_active_(false) { + window_tree_host_(window_tree_host), + is_active_(false), + click_component_(HTNOWHERE) { } 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(gfx::GetXDisplay(), - 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()) + aura::Window* target = static_cast<aura::Window*>(event->target()); + if (!target->delegate()) return; - aura::Window* target = static_cast<aura::Window*>(event->target()); + int previous_click_component = HTNOWHERE; int component = target->delegate()->GetNonClientComponent(event->location()); - if (component == HTCLIENT) - return; + if (event->IsLeftMouseButton()) { + previous_click_component = click_component_; + click_component_ = component; + } + + if (component == HTCAPTION) { + OnClickedCaption(event, previous_click_component); + } else if (component == HTMAXBUTTON) { + OnClickedMaximizeButton(event); + } else { + // Get the |x_root_window_| location out of the native event. + if (event->IsLeftMouseButton() && event->native_event()) { + const gfx::Point x_root_location = + ui::EventSystemLocationFromNative(event->native_event()); + if (target->GetProperty(aura::client::kCanResizeKey) && + DispatchHostWindowDragMovement(component, x_root_location)) { + event->StopPropagation(); + } + } + } +} + +void X11WindowEventFilter::OnClickedCaption(ui::MouseEvent* event, + int previous_click_component) { + aura::Window* target = static_cast<aura::Window*>(event->target()); + + if (event->IsMiddleMouseButton()) { + LinuxUI::NonClientMiddleClickAction action = + LinuxUI::MIDDLE_CLICK_ACTION_LOWER; + LinuxUI* linux_ui = LinuxUI::instance(); + if (linux_ui) + action = linux_ui->GetNonClientMiddleClickAction(); + + switch (action) { + case LinuxUI::MIDDLE_CLICK_ACTION_NONE: + break; + case LinuxUI::MIDDLE_CLICK_ACTION_LOWER: + XLowerWindow(xdisplay_, xwindow_); + break; + case LinuxUI::MIDDLE_CLICK_ACTION_MINIMIZE: + window_tree_host_->Minimize(); + break; + case LinuxUI::MIDDLE_CLICK_ACTION_TOGGLE_MAXIMIZE: + if (target->GetProperty(aura::client::kCanMaximizeKey)) + ToggleMaximizedState(); + break; + } - if (event->flags() & ui::EF_IS_DOUBLE_CLICK && component == HTCAPTION) { - // Our event is a double click in the caption area. We are responsible for - // dispatching this as a minimize/maximize on X11 (Windows converts this to - // min/max events for us). - if (root_window_host_->IsMaximized()) - root_window_host_->Restore(); - else - root_window_host_->Maximize(); event->SetHandled(); return; } + if (event->IsLeftMouseButton() && event->flags() & ui::EF_IS_DOUBLE_CLICK) { + click_component_ = HTNOWHERE; + if (target->GetProperty(aura::client::kCanMaximizeKey) && + previous_click_component == HTCAPTION) { + // Our event is a double click in the caption area in a window that can be + // maximized. We are responsible for dispatching this as a minimize/ + // maximize on X11 (Windows converts this to min/max events for us). + ToggleMaximizedState(); + event->SetHandled(); + return; + } + } + // Get the |x_root_window_| location out of the native event. - if (event->native_event()) { + if (event->IsLeftMouseButton() && event->native_event()) { const gfx::Point x_root_location = ui::EventSystemLocationFromNative(event->native_event()); - if (DispatchHostWindowDragMovement(component, x_root_location)) + if (DispatchHostWindowDragMovement(HTCAPTION, x_root_location)) event->StopPropagation(); } } +void X11WindowEventFilter::OnClickedMaximizeButton(ui::MouseEvent* event) { + aura::Window* target = static_cast<aura::Window*>(event->target()); + views::Widget* widget = views::Widget::GetWidgetForNativeView(target); + if (!widget) + return; + + gfx::Screen* screen = gfx::Screen::GetNativeScreen(); + gfx::Rect display_work_area = + screen->GetDisplayNearestWindow(target).work_area(); + gfx::Rect bounds = widget->GetWindowBoundsInScreen(); + if (event->IsMiddleMouseButton()) { + bounds.set_y(display_work_area.y()); + bounds.set_height(display_work_area.height()); + widget->SetBounds(bounds); + event->StopPropagation(); + } else if (event->IsRightMouseButton()) { + bounds.set_x(display_work_area.x()); + bounds.set_width(display_work_area.width()); + widget->SetBounds(bounds); + event->StopPropagation(); + } +} + +void X11WindowEventFilter::ToggleMaximizedState() { + if (window_tree_host_->IsMaximized()) + window_tree_host_->Restore(); + else + window_tree_host_->Maximize(); +} + bool X11WindowEventFilter::DispatchHostWindowDragMovement( int hittest, const gfx::Point& screen_location) { 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 index d172958cc8f..920733f154f 100644 --- a/chromium/ui/views/widget/desktop_aura/x11_window_event_filter.h +++ b/chromium/ui/views/widget/desktop_aura/x11_window_event_filter.h @@ -6,8 +6,6 @@ #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" @@ -17,7 +15,6 @@ #include "ui/views/views_export.h" namespace aura { -class RootWindow; class Window; } @@ -26,23 +23,28 @@ class Point; } namespace views { -class DesktopRootWindowHost; +class DesktopWindowTreeHost; class NativeWidgetAura; // An EventFilter that sets properties on X11 windows. class VIEWS_EXPORT X11WindowEventFilter : public ui::EventHandler { public: - X11WindowEventFilter(aura::RootWindow* root_window, - DesktopRootWindowHost* root_window_host); + explicit X11WindowEventFilter(DesktopWindowTreeHost* window_tree_host); 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: + // Called when the user clicked the caption area. + void OnClickedCaption(ui::MouseEvent* event, + int previous_click_component); + + // Called when the user clicked the maximize button. + void OnClickedMaximizeButton(ui::MouseEvent* event); + + void ToggleMaximizedState(); + // 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, @@ -57,11 +59,18 @@ class VIEWS_EXPORT X11WindowEventFilter : public ui::EventHandler { ui::X11AtomCache atom_cache_; - DesktopRootWindowHost* root_window_host_; + DesktopWindowTreeHost* window_tree_host_; // True if |xwindow_| is the current _NET_ACTIVE_WINDOW. bool is_active_; + // The non-client component for the target of a MouseEvent. Mouse events can + // be destructive to the window tree, which can cause the component of a + // ui::EF_IS_DOUBLE_CLICK event to no longer be the same as that of the + // initial click. Acting on a double click should only occur for matching + // components. + int click_component_; + DISALLOW_COPY_AND_ASSIGN(X11WindowEventFilter); }; diff --git a/chromium/ui/views/widget/drop_target_win.cc b/chromium/ui/views/widget/drop_target_win.cc deleted file mode 100644 index 56b0be1aef1..00000000000 --- a/chromium/ui/views/widget/drop_target_win.cc +++ /dev/null @@ -1,63 +0,0 @@ -// 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::ConvertPointFromScreen(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::ConvertPointFromScreen(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 deleted file mode 100644 index 742675122df..00000000000 --- a/chromium/ui/views/widget/drop_target_win.h +++ /dev/null @@ -1,55 +0,0 @@ -// 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 index 1292692d9f3..1220671beb8 100644 --- a/chromium/ui/views/widget/monitor_win.cc +++ b/chromium/ui/views/widget/monitor_win.cc @@ -4,10 +4,9 @@ #include "ui/views/widget/monitor_win.h" -#include <shellapi.h> +#include <windows.h> #include "base/logging.h" -#include "base/win/win_util.h" #include "ui/gfx/rect.h" namespace views { @@ -16,24 +15,12 @@ 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); - base::win::GetMonitorInfoWrapper(monitor, &mi); + MONITORINFO mi = { sizeof(MONITORINFO) }; + 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 index 87bdc4e00c2..ef4d3dde613 100644 --- a/chromium/ui/views/widget/monitor_win.h +++ b/chromium/ui/views/widget/monitor_win.h @@ -5,8 +5,6 @@ #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 { @@ -19,14 +17,6 @@ namespace views { // 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 index 39255ffc9c1..d01265f81f8 100644 --- a/chromium/ui/views/widget/native_widget.h +++ b/chromium/ui/views/widget/native_widget.h @@ -7,10 +7,6 @@ #include "ui/views/widget/widget.h" -namespace ui { -class EventHandler; -} - namespace views { namespace internal { class NativeWidgetPrivate; @@ -30,9 +26,6 @@ class VIEWS_EXPORT NativeWidget { public: virtual ~NativeWidget() {} - // Retrieves the event handler - virtual ui::EventHandler* GetEventHandler() = 0; - private: friend class Widget; diff --git a/chromium/ui/views/widget/native_widget_aura.cc b/chromium/ui/views/widget/native_widget_aura.cc index ffedac5c15a..150a1a42527 100644 --- a/chromium/ui/views/widget/native_widget_aura.cc +++ b/chromium/ui/views/widget/native_widget_aura.cc @@ -7,32 +7,28 @@ #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/focus_client.h" #include "ui/aura/client/screen_position_client.h" -#include "ui/aura/client/window_move_client.h" #include "ui/aura/client/window_tree_client.h" -#include "ui/aura/client/window_types.h" #include "ui/aura/env.h" -#include "ui/aura/root_window.h" #include "ui/aura/window.h" +#include "ui/aura/window_event_dispatcher.h" #include "ui/aura/window_observer.h" #include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/base/ui_base_switches_util.h" #include "ui/base/ui_base_types.h" #include "ui/compositor/layer.h" #include "ui/events/event.h" #include "ui/gfx/canvas.h" -#include "ui/gfx/font.h" +#include "ui/gfx/font_list.h" #include "ui/gfx/screen.h" #include "ui/native_theme/native_theme_aura.h" -#include "ui/views/corewm/window_util.h" #include "ui/views/drag_utils.h" #include "ui/views/ime/input_method_bridge.h" +#include "ui/views/ime/null_input_method.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_delegate.h" #include "ui/views/widget/root_view.h" @@ -40,20 +36,27 @@ #include "ui/views/widget/widget_aura_utils.h" #include "ui/views/widget/widget_delegate.h" #include "ui/views/widget/window_reorderer.h" +#include "ui/wm/core/shadow_types.h" +#include "ui/wm/core/window_util.h" +#include "ui/wm/public/activation_client.h" +#include "ui/wm/public/drag_drop_client.h" +#include "ui/wm/public/window_move_client.h" +#include "ui/wm/public/window_types.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" +#include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h" #endif #if defined(USE_X11) && !defined(OS_CHROMEOS) -#include "ui/views/widget/desktop_aura/desktop_root_window_host_x11.h" +#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" #endif #if !defined(OS_CHROMEOS) -#include "ui/views/widget/desktop_aura/desktop_root_window_host.h" +#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" +#include "ui/views/widget/desktop_aura/desktop_window_tree_host.h" #endif namespace views { @@ -74,7 +77,6 @@ NativeWidgetAura::NativeWidgetAura(internal::NativeWidgetDelegate* 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) { @@ -83,19 +85,6 @@ NativeWidgetAura::NativeWidgetAura(internal::NativeWidgetDelegate* delegate) } // 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 -} - -// static void NativeWidgetAura::RegisterNativeWidgetForWindow( internal::NativeWidgetPrivate* native_widget, aura::Window* window) { @@ -120,6 +109,10 @@ void NativeWidgetAura::InitNativeWidget(const Widget::InitParams& params) { window_->SetTransparent( params.opacity == Widget::InitParams::TRANSLUCENT_WINDOW); window_->Init(params.layer_type); + if (params.shadow_type == Widget::InitParams::SHADOW_TYPE_NONE) + SetShadowType(window_, wm::SHADOW_TYPE_NONE); + else if (params.shadow_type == Widget::InitParams::SHADOW_TYPE_DROP) + SetShadowType(window_, wm::SHADOW_TYPE_RECTANGULAR); if (params.type == Widget::InitParams::TYPE_CONTROL) window_->Show(); @@ -131,8 +124,8 @@ void NativeWidgetAura::InitNativeWidget(const Widget::InitParams& params) { 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 (parent && parent->type() != ui::wm::WINDOW_TYPE_UNKNOWN) { + wm::AddTransientChild(parent, window_); if (!context) context = parent; parent = NULL; @@ -150,6 +143,13 @@ void NativeWidgetAura::InitNativeWidget(const Widget::InitParams& params) { } } + // Set properties before addeing to the parent so that its layout manager + // sees the correct values. + window_->SetProperty(aura::client::kCanMaximizeKey, + GetWidget()->widget_delegate()->CanMaximize()); + window_->SetProperty(aura::client::kCanResizeKey, + GetWidget()->widget_delegate()->CanResize()); + if (parent) { parent->AddChild(window_); } else { @@ -165,9 +165,6 @@ void NativeWidgetAura::InitNativeWidget(const Widget::InitParams& params) { 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 (params.type != Widget::InitParams::TYPE_TOOLTIP) tooltip_manager_.reset(new views::TooltipManagerAura(GetWidget())); @@ -180,11 +177,6 @@ void NativeWidgetAura::InitNativeWidget(const Widget::InitParams& params) { 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())); } @@ -198,6 +190,10 @@ bool NativeWidgetAura::ShouldUseNativeFrame() const { return false; } +bool NativeWidgetAura::ShouldWindowContentsBeTransparent() const { + return false; +} + void NativeWidgetAura::FrameTypeChanged() { // This is called when the Theme has changed; forward the event to the root // widget. @@ -277,6 +273,10 @@ bool NativeWidgetAura::HasCapture() const { InputMethod* NativeWidgetAura::CreateInputMethod() { if (!window_) return NULL; + + if (switches::IsTextInputFocusManagerEnabled()) + return new NullInputMethod(); + aura::Window* root_window = window_->GetRootWindow(); ui::InputMethod* host = root_window->GetProperty(aura::client::kRootWindowInputMethodKey); @@ -287,6 +287,11 @@ internal::InputMethodDelegate* NativeWidgetAura::GetInputMethodDelegate() { return this; } +ui::InputMethod* NativeWidgetAura::GetHostInputMethod() { + aura::Window* root_window = window_->GetRootWindow(); + return root_window->GetProperty(aura::client::kRootWindowInputMethodKey); +} + void NativeWidgetAura::CenterWindow(const gfx::Size& size) { if (!window_) return; @@ -311,9 +316,9 @@ void NativeWidgetAura::CenterWindow(const gfx::Size& size) { // 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(); + if (wm::GetTransientParent(window_)) { + gfx::Rect transient_parent_rect = + wm::GetTransientParent(window_)->GetBoundsInRootWindow(); transient_parent_rect.Intersect(work_area); if (transient_parent_rect.height() >= size.height() && transient_parent_rect.width() >= size.width()) @@ -346,7 +351,7 @@ void NativeWidgetAura::GetWindowPlacement( ui::SHOW_STATE_DEFAULT; } -bool NativeWidgetAura::SetWindowTitle(const string16& title) { +bool NativeWidgetAura::SetWindowTitle(const base::string16& title) { if (!window_) return false; if (window_->title() == title) @@ -462,7 +467,7 @@ void NativeWidgetAura::CloseNow() { } void NativeWidgetAura::Show() { - ShowWithWindowState(ui::SHOW_STATE_INACTIVE); + ShowWithWindowState(ui::SHOW_STATE_NORMAL); } void NativeWidgetAura::Hide() { @@ -483,17 +488,13 @@ 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 (delegate_->CanActivate()) { 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(); + // SHOW_STATE_INACTIVE. If the window has to stay inactive, the method will + // do the right thing. + SetInitialFocus(state); } } @@ -523,7 +524,7 @@ void NativeWidgetAura::Deactivate() { } bool NativeWidgetAura::IsActive() const { - return window_ && corewm::IsActiveWindow(window_); + return window_ && wm::IsActiveWindow(window_); } void NativeWidgetAura::SetAlwaysOnTop(bool on_top) { @@ -535,6 +536,10 @@ bool NativeWidgetAura::IsAlwaysOnTop() const { return window_ && window_->GetProperty(aura::client::kAlwaysOnTopKey); } +void NativeWidgetAura::SetVisibleOnAllWorkspaces(bool always_visible) { + // Not implemented on chromeos or for child widgets. +} + void NativeWidgetAura::Maximize() { if (window_) window_->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); @@ -642,27 +647,32 @@ Widget::MoveLoopResult NativeWidgetAura::RunMoveLoop( Widget::MoveLoopEscapeBehavior escape_behavior) { // |escape_behavior| is only needed on windows when running the native message // loop. - if (window_ && 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; - } + if (!window_ || !window_->GetRootWindow()) + return Widget::MOVE_LOOP_CANCELED; + aura::client::WindowMoveClient* move_client = + aura::client::GetWindowMoveClient(window_->GetRootWindow()); + if (!move_client) + return Widget::MOVE_LOOP_CANCELED; + + 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 (move_client->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_ && window_->parent() && - aura::client::GetWindowMoveClient(window_->parent())) { - aura::client::GetWindowMoveClient(window_->parent())->EndMoveLoop(); - } + if (!window_ || !window_->GetRootWindow()) + return; + aura::client::WindowMoveClient* move_client = + aura::client::GetWindowMoveClient(window_->GetRootWindow()); + if (move_client) + move_client->EndMoveLoop(); } void NativeWidgetAura::SetVisibilityChangedAnimationsEnabled(bool value) { @@ -672,7 +682,7 @@ void NativeWidgetAura::SetVisibilityChangedAnimationsEnabled(bool value) { ui::NativeTheme* NativeWidgetAura::GetNativeTheme() const { #if !defined(OS_CHROMEOS) - return DesktopRootWindowHost::GetNativeTheme(window_); + return DesktopWindowTreeHost::GetNativeTheme(window_); #else return ui::NativeThemeAura::instance(); #endif @@ -681,6 +691,14 @@ ui::NativeTheme* NativeWidgetAura::GetNativeTheme() const { void NativeWidgetAura::OnRootViewLayout() const { } +bool NativeWidgetAura::IsTranslucentWindowOpacitySupported() const { + return true; +} + +void NativeWidgetAura::RepostNativeEvent(gfx::NativeEvent native_event) { + OnEvent(native_event); +} + //////////////////////////////////////////////////////////////////////////////// // NativeWidgetAura, views::InputMethodDelegate implementation: @@ -700,13 +718,22 @@ gfx::Size NativeWidgetAura::GetMinimumSize() const { } gfx::Size NativeWidgetAura::GetMaximumSize() const { + // If a window have a maximum size, the window should not be + // maximizable. + DCHECK(delegate_->GetMaximumSize().IsEmpty() || + !window_->GetProperty(aura::client::kCanMaximizeKey)); return delegate_->GetMaximumSize(); } void NativeWidgetAura::OnBoundsChanged(const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) { - if (old_bounds.origin() != new_bounds.origin()) + // Assume that if the old bounds was completely empty a move happened. This + // handles the case of a maximize animation acquiring the layer (acquiring a + // layer results in clearing the bounds). + if (old_bounds.origin() != new_bounds.origin() || + (old_bounds == gfx::Rect(0, 0, 0, 0) && !new_bounds.IsEmpty())) { delegate_->OnNativeWidgetMove(); + } if (old_bounds.size() != new_bounds.size()) delegate_->OnNativeWidgetSizeChanged(new_bounds.size()); } @@ -755,7 +782,7 @@ bool NativeWidgetAura::ShouldDescendIntoChildForEventHandling( } bool NativeWidgetAura::CanFocus() { - return can_activate_; + return ShouldActivate(); } void NativeWidgetAura::OnCaptureLost() { @@ -770,14 +797,14 @@ void NativeWidgetAura::OnDeviceScaleFactorChanged(float device_scale_factor) { // Repainting with new scale factor will paint the content at the right scale. } -void NativeWidgetAura::OnWindowDestroying() { +void NativeWidgetAura::OnWindowDestroying(aura::Window* window) { delegate_->OnNativeWidgetDestroying(); // If the aura::Window is destroyed, we can no longer show tooltips. tooltip_manager_.reset(); } -void NativeWidgetAura::OnWindowDestroyed() { +void NativeWidgetAura::OnWindowDestroyed(aura::Window* window) { window_ = NULL; delegate_->OnNativeWidgetDestroyed(); if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) @@ -797,10 +824,6 @@ void NativeWidgetAura::GetHitTestMask(gfx::Path* mask) const { delegate_->GetHitTestMask(mask); } -void NativeWidgetAura::DidRecreateLayer(ui::Layer *old_layer, - ui::Layer *new_layer) { -} - //////////////////////////////////////////////////////////////////////////////// // NativeWidgetAura, ui::EventHandler implementation: @@ -818,6 +841,12 @@ void NativeWidgetAura::OnKeyEvent(ui::KeyEvent* event) { if (!window_->IsVisible()) return; GetWidget()->GetInputMethod()->DispatchKeyEvent(*event); + if (switches::IsTextInputFocusManagerEnabled()) { + FocusManager* focus_manager = GetWidget()->GetFocusManager(); + delegate_->OnKeyEvent(event); + if (!event->handled() && focus_manager) + focus_manager->OnKeyEvent(*event); + } event->SetHandled(); } @@ -840,12 +869,6 @@ void NativeWidgetAura::OnScrollEvent(ui::ScrollEvent* event) { delegate_->OnScrollEvent(event); } -void NativeWidgetAura::OnTouchEvent(ui::TouchEvent* event) { - DCHECK(window_); - DCHECK(window_->IsVisible() || event->IsEndingEvent()); - delegate_->OnTouchEvent(event); -} - void NativeWidgetAura::OnGestureEvent(ui::GestureEvent* event) { DCHECK(window_); DCHECK(window_->IsVisible() || event->IsEndingEvent()); @@ -856,7 +879,7 @@ void NativeWidgetAura::OnGestureEvent(ui::GestureEvent* event) { // NativeWidgetAura, aura::client::ActivationDelegate implementation: bool NativeWidgetAura::ShouldActivate() const { - return can_activate_ && delegate_->CanActivate(); + return delegate_->CanActivate(); } //////////////////////////////////////////////////////////////////////////////// @@ -872,8 +895,6 @@ void NativeWidgetAura::OnWindowActivated(aura::Window* gained_active, GetWidget()->GetFocusManager()->StoreFocusedView(true); } delegate_->OnNativeWidgetActivationChanged(window_ == gained_active); - if (IsVisible() && GetWidget()->non_client_view()) - GetWidget()->non_client_view()->SchedulePaint(); } //////////////////////////////////////////////////////////////////////////////// @@ -902,9 +923,7 @@ void NativeWidgetAura::OnWindowFocused(aura::Window* gained_focus, 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()); + delegate_->OnNativeBlur(gained_focus); } } @@ -936,13 +955,6 @@ int NativeWidgetAura::OnPerformDrop(const ui::DropTargetEvent& event) { } //////////////////////////////////////////////////////////////////////////////// -// NativeWidgetAura, NativeWidget implementation: - -ui::EventHandler* NativeWidgetAura::GetEventHandler() { - return this; -} - -//////////////////////////////////////////////////////////////////////////////// // NativeWidgetAura, protected: NativeWidgetAura::~NativeWidgetAura() { @@ -956,20 +968,15 @@ NativeWidgetAura::~NativeWidgetAura() { //////////////////////////////////////////////////////////////////////////////// // NativeWidgetAura, private: -void NativeWidgetAura::SetInitialFocus() { +void NativeWidgetAura::SetInitialFocus(ui::WindowShowState show_state) { // The window does not get keyboard messages unless we focus it. - if (!GetWidget()->SetInitialFocus()) + if (!GetWidget()->SetInitialFocus(show_state)) window_->Focus(); } //////////////////////////////////////////////////////////////////////////////// // Widget, public: -// static -void Widget::NotifyLocaleChanged() { - // Deliberately not implemented. -} - namespace { #if defined(OS_WIN) || (defined(USE_X11) && !defined(OS_CHROMEOS)) void CloseWindow(aura::Window* window) { @@ -988,7 +995,7 @@ void CloseWindow(aura::Window* window) { #if defined(OS_WIN) BOOL CALLBACK WindowCallbackProc(HWND hwnd, LPARAM lParam) { aura::Window* root_window = - DesktopRootWindowHostWin::GetContentWindowForHWND(hwnd); + DesktopWindowTreeHostWin::GetContentWindowForHWND(hwnd); CloseWindow(root_window); return TRUE; } @@ -1003,9 +1010,9 @@ void Widget::CloseAllSecondaryWidgets() { #if defined(USE_X11) && !defined(OS_CHROMEOS) std::vector<aura::Window*> open_windows = - DesktopRootWindowHostX11::GetAllOpenWindows(); + DesktopWindowTreeHostX11::GetAllOpenWindows(); std::for_each(open_windows.begin(), open_windows.end(), CloseWindow); - DesktopRootWindowHostX11::CleanUpWindowList(); + DesktopWindowTreeHostX11::CleanUpWindowList(); #endif } @@ -1076,7 +1083,7 @@ void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view, void NativeWidgetPrivate::GetAllOwnedWidgets(gfx::NativeView native_view, Widget::Widgets* owned) { const aura::Window::Windows& transient_children = - native_view->transient_children(); + wm::GetTransientChildren(native_view); for (aura::Window::Windows::const_iterator i = transient_children.begin(); i != transient_children.end(); ++i) { NativeWidgetPrivate* native_widget = static_cast<NativeWidgetPrivate*>( @@ -1142,5 +1149,18 @@ bool NativeWidgetPrivate::IsTouchDown() { return aura::Env::GetInstance()->is_touch_down(); } +// static +gfx::FontList NativeWidgetPrivate::GetWindowTitleFontList() { +#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::FontList(gfx::Font(caption_font)); +#else + return gfx::FontList(); +#endif +} + } // namespace internal } // namespace views diff --git a/chromium/ui/views/widget/native_widget_aura.h b/chromium/ui/views/widget/native_widget_aura.h index c79c003f995..dd6fafe8381 100644 --- a/chromium/ui/views/widget/native_widget_aura.h +++ b/chromium/ui/views/widget/native_widget_aura.h @@ -7,9 +7,6 @@ #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/cursor/cursor.h" @@ -17,12 +14,15 @@ #include "ui/views/ime/input_method_delegate.h" #include "ui/views/views_export.h" #include "ui/views/widget/native_widget_private.h" +#include "ui/wm/public/activation_change_observer.h" +#include "ui/wm/public/activation_delegate.h" +#include "ui/wm/public/drag_drop_delegate.h" namespace aura { class Window; } namespace gfx { -class Font; +class FontList; } namespace views { @@ -42,10 +42,6 @@ class VIEWS_EXPORT NativeWidgetAura public: explicit NativeWidgetAura(internal::NativeWidgetDelegate* delegate); - // TODO(beng): Find a better place for this, and the similar method on - // NativeWidgetWin. - static gfx::Font GetWindowTitleFont(); - // Called internally by NativeWidgetAura and DesktopNativeWidgetAura to // associate |native_widget| with |window|. static void RegisterNativeWidgetForWindow( @@ -56,6 +52,7 @@ class VIEWS_EXPORT NativeWidgetAura virtual void InitNativeWidget(const Widget::InitParams& params) OVERRIDE; virtual NonClientFrameView* CreateNonClientFrameView() OVERRIDE; virtual bool ShouldUseNativeFrame() const OVERRIDE; + virtual bool ShouldWindowContentsBeTransparent() const OVERRIDE; virtual void FrameTypeChanged() OVERRIDE; virtual Widget* GetWidget() OVERRIDE; virtual const Widget* GetWidget() const OVERRIDE; @@ -75,11 +72,12 @@ class VIEWS_EXPORT NativeWidgetAura virtual bool HasCapture() const OVERRIDE; virtual InputMethod* CreateInputMethod() OVERRIDE; virtual internal::InputMethodDelegate* GetInputMethodDelegate() OVERRIDE; + virtual ui::InputMethod* GetHostInputMethod() OVERRIDE; virtual void CenterWindow(const gfx::Size& size) OVERRIDE; virtual void GetWindowPlacement( gfx::Rect* bounds, ui::WindowShowState* maximized) const OVERRIDE; - virtual bool SetWindowTitle(const string16& title) OVERRIDE; + virtual bool SetWindowTitle(const base::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; @@ -105,6 +103,7 @@ class VIEWS_EXPORT NativeWidgetAura virtual bool IsActive() const OVERRIDE; virtual void SetAlwaysOnTop(bool always_on_top) OVERRIDE; virtual bool IsAlwaysOnTop() const OVERRIDE; + virtual void SetVisibleOnAllWorkspaces(bool always_visible) OVERRIDE; virtual void Maximize() OVERRIDE; virtual void Minimize() OVERRIDE; virtual bool IsMaximized() const OVERRIDE; @@ -133,6 +132,8 @@ class VIEWS_EXPORT NativeWidgetAura virtual void SetVisibilityChangedAnimationsEnabled(bool value) OVERRIDE; virtual ui::NativeTheme* GetNativeTheme() const OVERRIDE; virtual void OnRootViewLayout() const OVERRIDE; + virtual bool IsTranslucentWindowOpacitySupported() const OVERRIDE; + virtual void RepostNativeEvent(gfx::NativeEvent native_event) OVERRIDE; // Overridden from views::InputMethodDelegate: virtual void DispatchKeyEventPostIME(const ui::KeyEvent& key) OVERRIDE; @@ -151,19 +152,16 @@ class VIEWS_EXPORT NativeWidgetAura 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 OnWindowDestroying(aura::Window* window) OVERRIDE; + virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE; virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE; virtual bool HasHitTestMask() const OVERRIDE; virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE; - virtual void DidRecreateLayer(ui::Layer* old_layer, - ui::Layer* new_layer) 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: @@ -183,9 +181,6 @@ class VIEWS_EXPORT NativeWidgetAura virtual void OnDragExited() OVERRIDE; virtual int OnPerformDrop(const ui::DropTargetEvent& event) OVERRIDE; - // Overridden from NativeWidget: - virtual ui::EventHandler* GetEventHandler() OVERRIDE; - protected: virtual ~NativeWidgetAura(); @@ -194,7 +189,7 @@ class VIEWS_EXPORT NativeWidgetAura private: class ActiveWindowObserver; - void SetInitialFocus(); + void SetInitialFocus(ui::WindowShowState show_state); internal::NativeWidgetDelegate* delegate_; @@ -210,9 +205,6 @@ class VIEWS_EXPORT NativeWidgetAura // instance. base::WeakPtrFactory<NativeWidgetAura> close_widget_factory_; - // Can we be made active? - bool can_activate_; - // Are we in the destructor? bool destroying_; diff --git a/chromium/ui/views/widget/native_widget_aura_unittest.cc b/chromium/ui/views/widget/native_widget_aura_unittest.cc index f0f34c76cbd..3c6d3cc49ad 100644 --- a/chromium/ui/views/widget/native_widget_aura_unittest.cc +++ b/chromium/ui/views/widget/native_widget_aura_unittest.cc @@ -12,14 +12,16 @@ #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/test/aura_test_base.h" #include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" #include "ui/events/event.h" +#include "ui/events/event_utils.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" +#include "ui/wm/core/default_activation_client.h" namespace views { namespace { @@ -32,40 +34,29 @@ NativeWidgetAura* Init(aura::Window* parent, Widget* widget) { return static_cast<NativeWidgetAura*>(widget->native_widget()); } -class NativeWidgetAuraTest : public testing::Test { +class NativeWidgetAuraTest : public aura::test::AuraTestBase { 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)); - dispatcher()->SetHostSize(gfx::Size(640, 480)); + AuraTestBase::SetUp(); + new wm::DefaultActivationClient(root_window()); + host()->SetBounds(gfx::Rect(640, 480)); } - virtual void TearDown() OVERRIDE { - message_loop_.RunUntilIdle(); - aura_test_helper_->TearDown(); - } - - protected: - aura::Window* root_window() { return aura_test_helper_->root_window(); } - aura::RootWindow* dispatcher() { return aura_test_helper_->dispatcher(); } 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. + // Make a parent window larger than the host represented by + // WindowEventDispatcher. scoped_ptr<aura::Window> parent(new aura::Window(NULL)); - parent->Init(ui::LAYER_NOT_DRAWN); + parent->Init(aura::WINDOW_LAYER_NOT_DRAWN); parent->SetBounds(gfx::Rect(0, 0, 1024, 800)); - scoped_ptr<Widget> widget(new Widget()); + scoped_ptr<Widget> widget(new Widget()); NativeWidgetAura* window = Init(parent.get(), widget.get()); window->CenterWindow(gfx::Size(100, 100)); @@ -77,9 +68,10 @@ TEST_F(NativeWidgetAuraTest, CenterWindowLargeParent) { } TEST_F(NativeWidgetAuraTest, CenterWindowSmallParent) { - // Make a parent window smaller than the host represented by rootwindow. + // Make a parent window smaller than the host represented by + // WindowEventDispatcher. scoped_ptr<aura::Window> parent(new aura::Window(NULL)); - parent->Init(ui::LAYER_NOT_DRAWN); + parent->Init(aura::WINDOW_LAYER_NOT_DRAWN); parent->SetBounds(gfx::Rect(0, 0, 480, 320)); scoped_ptr<Widget> widget(new Widget()); NativeWidgetAura* window = Init(parent.get(), widget.get()); @@ -94,10 +86,10 @@ TEST_F(NativeWidgetAuraTest, CenterWindowSmallParent) { // 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. + // Make a parent window smaller than the host represented by + // WindowEventDispatcher and offset it slightly from the origin. scoped_ptr<aura::Window> parent(new aura::Window(NULL)); - parent->Init(ui::LAYER_NOT_DRAWN); + parent->Init(aura::WINDOW_LAYER_NOT_DRAWN); parent->SetBounds(gfx::Rect(20, 40, 480, 320)); scoped_ptr<Widget> widget(new Widget()); NativeWidgetAura* window = Init(parent.get(), widget.get()); @@ -108,30 +100,39 @@ TEST_F(NativeWidgetAuraTest, CenterWindowSmallParentNotAtOrigin) { widget->CloseNow(); } +class TestLayoutManagerBase : public aura::LayoutManager { + public: + TestLayoutManagerBase() {} + virtual ~TestLayoutManagerBase() {} + + // 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: + DISALLOW_COPY_AND_ASSIGN(TestLayoutManagerBase); +}; + // Used by ShowMaximizedDoesntBounceAround. See it for details. -class TestLayoutManager : public aura::LayoutManager { +class MaximizeLayoutManager : public TestLayoutManagerBase { public: - TestLayoutManager() {} + MaximizeLayoutManager() {} + virtual ~MaximizeLayoutManager() {} - virtual void OnWindowResized() OVERRIDE { - } + private: + // aura::LayoutManager: 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); + DISALLOW_COPY_AND_ASSIGN(MaximizeLayoutManager); }; // This simulates BrowserView, which creates a custom RootView so that @@ -168,7 +169,7 @@ class TestWidget : public views::Widget { // leads to noticable flashes. TEST_F(NativeWidgetAuraTest, ShowMaximizedDoesntBounceAround) { root_window()->SetBounds(gfx::Rect(0, 0, 640, 480)); - root_window()->SetLayoutManager(new TestLayoutManager); + root_window()->SetLayoutManager(new MaximizeLayoutManager); scoped_ptr<TestWidget> widget(new TestWidget()); Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; @@ -181,6 +182,70 @@ TEST_F(NativeWidgetAuraTest, ShowMaximizedDoesntBounceAround) { widget->CloseNow(); } +class PropertyTestLayoutManager : public TestLayoutManagerBase { + public: + PropertyTestLayoutManager() : added_(false) {} + virtual ~PropertyTestLayoutManager() {} + + bool added() const { return added_; } + + private: + // aura::LayoutManager: + virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE { + EXPECT_TRUE(child->GetProperty(aura::client::kCanMaximizeKey)); + EXPECT_TRUE(child->GetProperty(aura::client::kCanResizeKey)); + added_ = true; + } + + bool added_; + + DISALLOW_COPY_AND_ASSIGN(PropertyTestLayoutManager); +}; + +class PropertyTestWidgetDelegate : public views::WidgetDelegate { + public: + explicit PropertyTestWidgetDelegate(Widget* widget) : widget_(widget) {} + virtual ~PropertyTestWidgetDelegate() {} + + private: + // views::WidgetDelegate: + virtual bool CanMaximize() const OVERRIDE { + return true; + } + virtual bool CanResize() const OVERRIDE { + return true; + } + virtual void DeleteDelegate() OVERRIDE { + delete this; + } + virtual Widget* GetWidget() OVERRIDE { + return widget_; + } + virtual const Widget* GetWidget() const OVERRIDE { + return widget_; + } + + Widget* widget_; + DISALLOW_COPY_AND_ASSIGN(PropertyTestWidgetDelegate); +}; + +// Verifies that the kCanMaximizeKey/kCanReizeKey have the correct +// value when added to the layout manager. +TEST_F(NativeWidgetAuraTest, TestPropertiesWhenAddedToLayout) { + root_window()->SetBounds(gfx::Rect(0, 0, 640, 480)); + PropertyTestLayoutManager* layout_manager = new PropertyTestLayoutManager(); + root_window()->SetLayoutManager(layout_manager); + scoped_ptr<TestWidget> widget(new TestWidget()); + Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.delegate = new PropertyTestWidgetDelegate(widget.get()); + params.parent = NULL; + params.context = root_window(); + widget->Init(params); + EXPECT_TRUE(layout_manager->added()); + widget->CloseNow(); +} + TEST_F(NativeWidgetAuraTest, GetClientAreaScreenBounds) { // Create a widget. Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); @@ -198,8 +263,6 @@ TEST_F(NativeWidgetAuraTest, GetClientAreaScreenBounds) { EXPECT_EQ(400, client_bounds.height()); } -namespace { - // View subclass that tracks whether it has gotten a gesture event. class GestureTrackingView : public views::View { public: @@ -235,8 +298,6 @@ class GestureTrackingView : public views::View { 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) { @@ -256,9 +317,11 @@ TEST_F(NativeWidgetAuraTest, DontCaptureOnGesture) { widget->SetContentsView(view); widget->Show(); - ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(41, 51), 1, - base::TimeDelta()); - dispatcher()->AsRootWindowHostDelegate()->OnHostTouchEvent(&press); + ui::TouchEvent press( + ui::ET_TOUCH_PRESSED, gfx::Point(41, 51), 1, ui::EventTimeForNow()); + ui::EventDispatchDetails details = + event_processor()->OnEventFromSource(&press); + ASSERT_FALSE(details.dispatcher_destroyed); // Both views should get the press. EXPECT_TRUE(view->got_gesture_event()); EXPECT_TRUE(child->got_gesture_event()); @@ -269,9 +332,10 @@ TEST_F(NativeWidgetAuraTest, DontCaptureOnGesture) { // 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()); - dispatcher()->AsRootWindowHostDelegate()->OnHostTouchEvent(&release); + ui::TouchEvent release( + ui::ET_TOUCH_RELEASED, gfx::Point(250, 251), 1, ui::EventTimeForNow()); + details = event_processor()->OnEventFromSource(&release); + ASSERT_FALSE(details.dispatcher_destroyed); EXPECT_TRUE(view->got_gesture_event()); EXPECT_FALSE(child->got_gesture_event()); view->clear_got_gesture_event(); @@ -281,39 +345,6 @@ TEST_F(NativeWidgetAuraTest, DontCaptureOnGesture) { 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()); - dispatcher()->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()); - dispatcher()->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|. @@ -399,15 +430,53 @@ TEST_F(NativeWidgetAuraTest, FlashFrame) { TEST_F(NativeWidgetAuraTest, NoCrashOnThemeAfterClose) { scoped_ptr<aura::Window> parent(new aura::Window(NULL)); - parent->Init(ui::LAYER_NOT_DRAWN); + parent->Init(aura::WINDOW_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(); + Init(parent.get(), widget.get()); + widget->Show(); + widget->Close(); base::MessageLoop::current()->RunUntilIdle(); widget->GetNativeTheme(); // Shouldn't crash. } +// Used to track calls to WidgetDelegate::OnWidgetMove(). +class MoveTestWidgetDelegate : public WidgetDelegateView { + public: + MoveTestWidgetDelegate() : got_move_(false) {} + virtual ~MoveTestWidgetDelegate() {} + + void ClearGotMove() { got_move_ = false; } + bool got_move() const { return got_move_; } + + // WidgetDelegate overrides: + virtual void OnWidgetMove() OVERRIDE { got_move_ = true; } + + private: + bool got_move_; + + DISALLOW_COPY_AND_ASSIGN(MoveTestWidgetDelegate); +}; + +// This test simulates what happens when a window is normally maximized. That +// is, it's layer is acquired for animation then the window is maximized. +// Acquiring the layer resets the bounds of the window. This test verifies the +// Widget is still notified correctly of a move in this case. +TEST_F(NativeWidgetAuraTest, OnWidgetMovedInvokedAfterAcquireLayer) { + // |delegate| deletes itself when the widget is destroyed. + MoveTestWidgetDelegate* delegate = new MoveTestWidgetDelegate; + Widget* widget = + Widget::CreateWindowWithContextAndBounds(delegate, + root_window(), + gfx::Rect(10, 10, 100, 200)); + widget->Show(); + delegate->ClearGotMove(); + // Simulate a maximize with animation. + delete widget->GetNativeView()->RecreateLayer().release(); + widget->SetBounds(gfx::Rect(0, 0, 500, 500)); + EXPECT_TRUE(delegate->got_move()); + widget->CloseNow(); +} + } // namespace } // namespace views diff --git a/chromium/ui/views/widget/native_widget_delegate.h b/chromium/ui/views/widget/native_widget_delegate.h index 914b575f30d..c18b5fc8015 100644 --- a/chromium/ui/views/widget/native_widget_delegate.h +++ b/chromium/ui/views/widget/native_widget_delegate.h @@ -80,10 +80,10 @@ class VIEWS_EXPORT NativeWidgetDelegate { virtual void OnNativeWidgetDestroyed() = 0; // Returns the smallest size the window can be resized to by the user. - virtual gfx::Size GetMinimumSize() = 0; + virtual gfx::Size GetMinimumSize() const = 0; // Returns the largest size the window can be resized to by the user. - virtual gfx::Size GetMaximumSize() = 0; + virtual gfx::Size GetMaximumSize() const = 0; // Called when the NativeWidget changed position. virtual void OnNativeWidgetMove() = 0; @@ -116,7 +116,6 @@ class VIEWS_EXPORT NativeWidgetDelegate { 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; @@ -140,6 +139,14 @@ class VIEWS_EXPORT NativeWidgetDelegate { // virtual Widget* AsWidget() = 0; virtual const Widget* AsWidget() const = 0; + + // Sets-up the focus manager with the view that should have focus when the + // window is shown the first time. It takes the intended |show_state| of the + // window in order to decide whether the window should be focused now or + // later. Returns true if the initial focus has been set or the window should + // not set the initial focus, or false if the caller should set the initial + // focus (if any). + virtual bool SetInitialFocus(ui::WindowShowState show_state) = 0; }; } // namespace internal diff --git a/chromium/ui/views/widget/native_widget_mac.h b/chromium/ui/views/widget/native_widget_mac.h new file mode 100644 index 00000000000..64cb9a3bea8 --- /dev/null +++ b/chromium/ui/views/widget/native_widget_mac.h @@ -0,0 +1,117 @@ +// Copyright 2014 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_MAC_H_ +#define UI_VIEWS_WIDGET_NATIVE_WIDGET_MAC_H_ + +#include "ui/gfx/native_widget_types.h" +#include "ui/views/widget/native_widget_private.h" + +namespace views { + +class BridgedNativeWidget; + +class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate { + public: + NativeWidgetMac(internal::NativeWidgetDelegate* delegate); + virtual ~NativeWidgetMac(); + + protected: + // internal::NativeWidgetPrivate: + virtual void InitNativeWidget(const Widget::InitParams& params) OVERRIDE; + virtual NonClientFrameView* CreateNonClientFrameView() OVERRIDE; + virtual bool ShouldUseNativeFrame() const OVERRIDE; + virtual bool ShouldWindowContentsBeTransparent() 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 ui::InputMethod* GetHostInputMethod() OVERRIDE; + virtual void CenterWindow(const gfx::Size& size) OVERRIDE; + virtual void GetWindowPlacement( + gfx::Rect* bounds, + ui::WindowShowState* maximized) const OVERRIDE; + virtual bool SetWindowTitle(const base::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 bool IsAlwaysOnTop() const OVERRIDE; + virtual void SetVisibleOnAllWorkspaces(bool always_visible) 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 Widget::MoveLoopResult RunMoveLoop( + const gfx::Vector2d& drag_offset, + Widget::MoveLoopSource source, + Widget::MoveLoopEscapeBehavior escape_behavior) OVERRIDE; + virtual void EndMoveLoop() OVERRIDE; + virtual void SetVisibilityChangedAnimationsEnabled(bool value) OVERRIDE; + virtual ui::NativeTheme* GetNativeTheme() const OVERRIDE; + virtual void OnRootViewLayout() const OVERRIDE; + virtual bool IsTranslucentWindowOpacitySupported() const OVERRIDE; + virtual void RepostNativeEvent(gfx::NativeEvent native_event) OVERRIDE; + + private: + internal::NativeWidgetDelegate* delegate_; + scoped_ptr<BridgedNativeWidget> bridge_; + + DISALLOW_COPY_AND_ASSIGN(NativeWidgetMac); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_NATIVE_WIDGET_MAC_H_ diff --git a/chromium/ui/views/widget/native_widget_mac.mm b/chromium/ui/views/widget/native_widget_mac.mm new file mode 100644 index 00000000000..37545b974cf --- /dev/null +++ b/chromium/ui/views/widget/native_widget_mac.mm @@ -0,0 +1,448 @@ +// Copyright 2014 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_mac.h" + +#import <Cocoa/Cocoa.h> + +#include "base/mac/scoped_nsobject.h" +#include "ui/gfx/font_list.h" +#include "ui/native_theme/native_theme.h" +#import "ui/views/cocoa/bridged_content_view.h" +#import "ui/views/cocoa/bridged_native_widget.h" + +namespace views { + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetMac, public: + +NativeWidgetMac::NativeWidgetMac(internal::NativeWidgetDelegate* delegate) + : delegate_(delegate), bridge_(new BridgedNativeWidget) { +} + +NativeWidgetMac::~NativeWidgetMac() { +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeWidgetMac, internal::NativeWidgetPrivate implementation: + +void NativeWidgetMac::InitNativeWidget(const Widget::InitParams& params) { + // TODO(tapted): Convert position into Cocoa's flipped coordinate space. + NSRect content_rect = + NSMakeRect(0, 0, params.bounds.width(), params.bounds.height()); + // TODO(tapted): Determine a good initial style mask from |params|. + NSInteger style_mask = NSTitledWindowMask | NSClosableWindowMask | + NSMiniaturizableWindowMask | NSResizableWindowMask; + base::scoped_nsobject<NSWindow> window( + [[NSWindow alloc] initWithContentRect:content_rect + styleMask:style_mask + backing:NSBackingStoreBuffered + defer:NO]); + bridge_->Init(window); +} + +NonClientFrameView* NativeWidgetMac::CreateNonClientFrameView() { + return NULL; +} + +bool NativeWidgetMac::ShouldUseNativeFrame() const { + return false; +} + +bool NativeWidgetMac::ShouldWindowContentsBeTransparent() const { + NOTIMPLEMENTED(); + return false; +} + +void NativeWidgetMac::FrameTypeChanged() { + NOTIMPLEMENTED(); +} + +Widget* NativeWidgetMac::GetWidget() { + return delegate_->AsWidget(); +} + +const Widget* NativeWidgetMac::GetWidget() const { + return delegate_->AsWidget(); +} + +gfx::NativeView NativeWidgetMac::GetNativeView() const { + return bridge_->ns_view(); +} + +gfx::NativeWindow NativeWidgetMac::GetNativeWindow() const { + return bridge_->ns_window(); +} + +Widget* NativeWidgetMac::GetTopLevelWidget() { + NOTIMPLEMENTED(); + return GetWidget(); +} + +const ui::Compositor* NativeWidgetMac::GetCompositor() const { + NOTIMPLEMENTED(); + return NULL; +} + +ui::Compositor* NativeWidgetMac::GetCompositor() { + NOTIMPLEMENTED(); + return NULL; +} + +ui::Layer* NativeWidgetMac::GetLayer() { + NOTIMPLEMENTED(); + return NULL; +} + +void NativeWidgetMac::ReorderNativeViews() { + bridge_->SetRootView(GetWidget()->GetRootView()); +} + +void NativeWidgetMac::ViewRemoved(View* view) { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::SetNativeWindowProperty(const char* name, void* value) { + NOTIMPLEMENTED(); +} + +void* NativeWidgetMac::GetNativeWindowProperty(const char* name) const { + NOTIMPLEMENTED(); + return NULL; +} + +TooltipManager* NativeWidgetMac::GetTooltipManager() const { + NOTIMPLEMENTED(); + return NULL; +} + +void NativeWidgetMac::SetCapture() { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::ReleaseCapture() { + NOTIMPLEMENTED(); +} + +bool NativeWidgetMac::HasCapture() const { + NOTIMPLEMENTED(); + return false; +} + +InputMethod* NativeWidgetMac::CreateInputMethod() { + return bridge_->CreateInputMethod(); +} + +internal::InputMethodDelegate* NativeWidgetMac::GetInputMethodDelegate() { + return bridge_.get(); +} + +ui::InputMethod* NativeWidgetMac::GetHostInputMethod() { + return bridge_->GetHostInputMethod(); +} + +void NativeWidgetMac::CenterWindow(const gfx::Size& size) { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::GetWindowPlacement(gfx::Rect* bounds, + ui::WindowShowState* maximized) const { + NOTIMPLEMENTED(); +} + +bool NativeWidgetMac::SetWindowTitle(const base::string16& title) { + NOTIMPLEMENTED(); + return false; +} + +void NativeWidgetMac::SetWindowIcons(const gfx::ImageSkia& window_icon, + const gfx::ImageSkia& app_icon) { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::InitModalType(ui::ModalType modal_type) { + NOTIMPLEMENTED(); +} + +gfx::Rect NativeWidgetMac::GetWindowBoundsInScreen() const { + NOTIMPLEMENTED(); + return gfx::Rect(); +} + +gfx::Rect NativeWidgetMac::GetClientAreaBoundsInScreen() const { + NOTIMPLEMENTED(); + return gfx::Rect(); +} + +gfx::Rect NativeWidgetMac::GetRestoredBounds() const { + NOTIMPLEMENTED(); + return gfx::Rect(); +} + +void NativeWidgetMac::SetBounds(const gfx::Rect& bounds) { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::SetSize(const gfx::Size& size) { + [bridge_->ns_window() setContentSize:NSMakeSize(size.width(), size.height())]; +} + +void NativeWidgetMac::StackAbove(gfx::NativeView native_view) { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::StackAtTop() { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::StackBelow(gfx::NativeView native_view) { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::SetShape(gfx::NativeRegion shape) { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::Close() { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::CloseNow() { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::Show() { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::Hide() { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::ShowMaximizedWithBounds( + const gfx::Rect& restored_bounds) { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::ShowWithWindowState(ui::WindowShowState state) { + NOTIMPLEMENTED(); +} + +bool NativeWidgetMac::IsVisible() const { + NOTIMPLEMENTED(); + return true; +} + +void NativeWidgetMac::Activate() { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::Deactivate() { + NOTIMPLEMENTED(); +} + +bool NativeWidgetMac::IsActive() const { + NOTIMPLEMENTED(); + return true; +} + +void NativeWidgetMac::SetAlwaysOnTop(bool always_on_top) { + NOTIMPLEMENTED(); +} + +bool NativeWidgetMac::IsAlwaysOnTop() const { + NOTIMPLEMENTED(); + return false; +} + +void NativeWidgetMac::SetVisibleOnAllWorkspaces(bool always_visible) { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::Maximize() { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::Minimize() { + NOTIMPLEMENTED(); +} + +bool NativeWidgetMac::IsMaximized() const { + NOTIMPLEMENTED(); + return false; +} + +bool NativeWidgetMac::IsMinimized() const { + NOTIMPLEMENTED(); + return false; +} + +void NativeWidgetMac::Restore() { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::SetFullscreen(bool fullscreen) { + NOTIMPLEMENTED(); +} + +bool NativeWidgetMac::IsFullscreen() const { + NOTIMPLEMENTED(); + return false; +} + +void NativeWidgetMac::SetOpacity(unsigned char opacity) { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::SetUseDragFrame(bool use_drag_frame) { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::FlashFrame(bool flash_frame) { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::RunShellDrag(View* view, + const ui::OSExchangeData& data, + const gfx::Point& location, + int operation, + ui::DragDropTypes::DragEventSource source) { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::SchedulePaintInRect(const gfx::Rect& rect) { + // TODO(tapted): This should use setNeedsDisplayInRect:, once the coordinate + // system of |rect| has been converted. + [bridge_->ns_view() setNeedsDisplay:YES]; +} + +void NativeWidgetMac::SetCursor(gfx::NativeCursor cursor) { + NOTIMPLEMENTED(); +} + +bool NativeWidgetMac::IsMouseEventsEnabled() const { + NOTIMPLEMENTED(); + return true; +} + +void NativeWidgetMac::ClearNativeFocus() { + NOTIMPLEMENTED(); +} + +gfx::Rect NativeWidgetMac::GetWorkAreaBoundsInScreen() const { + NOTIMPLEMENTED(); + return gfx::Rect(); +} + +Widget::MoveLoopResult NativeWidgetMac::RunMoveLoop( + const gfx::Vector2d& drag_offset, + Widget::MoveLoopSource source, + Widget::MoveLoopEscapeBehavior escape_behavior) { + NOTIMPLEMENTED(); + return Widget::MOVE_LOOP_CANCELED; +} + +void NativeWidgetMac::EndMoveLoop() { + NOTIMPLEMENTED(); +} + +void NativeWidgetMac::SetVisibilityChangedAnimationsEnabled(bool value) { + NOTIMPLEMENTED(); +} + +ui::NativeTheme* NativeWidgetMac::GetNativeTheme() const { + return ui::NativeTheme::instance(); +} + +void NativeWidgetMac::OnRootViewLayout() const { + NOTIMPLEMENTED(); +} + +bool NativeWidgetMac::IsTranslucentWindowOpacitySupported() const { + return false; +} + +void NativeWidgetMac::RepostNativeEvent(gfx::NativeEvent native_event) { + NOTIMPLEMENTED(); +} + +//////////////////////////////////////////////////////////////////////////////// +// Widget, public: + +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 NativeWidgetMac(delegate); +} + +// static +NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeView( + gfx::NativeView native_view) { + NOTIMPLEMENTED(); + return NULL; +} + +// static +NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeWindow( + gfx::NativeWindow native_window) { + NOTIMPLEMENTED(); + return NULL; +} + +// static +NativeWidgetPrivate* NativeWidgetPrivate::GetTopLevelNativeWidget( + gfx::NativeView native_view) { + NOTIMPLEMENTED(); + return NULL; +} + +// static +void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view, + Widget::Widgets* children) { + NOTIMPLEMENTED(); +} + +// static +void NativeWidgetPrivate::GetAllOwnedWidgets(gfx::NativeView native_view, + Widget::Widgets* owned) { + NOTIMPLEMENTED(); +} + +// static +void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view, + gfx::NativeView new_parent) { + NOTIMPLEMENTED(); +} + +// static +bool NativeWidgetPrivate::IsMouseButtonDown() { + NOTIMPLEMENTED(); + return false; +} + +// static +bool NativeWidgetPrivate::IsTouchDown() { + NOTIMPLEMENTED(); + return false; +} + +// static +gfx::FontList NativeWidgetPrivate::GetWindowTitleFontList() { + NOTIMPLEMENTED(); + return gfx::FontList(); +} + +} // namespace internal +} // namespace views diff --git a/chromium/ui/views/widget/native_widget_private.h b/chromium/ui/views/widget/native_widget_private.h index 2cee9533a6b..8c9233a3ff8 100644 --- a/chromium/ui/views/widget/native_widget_private.h +++ b/chromium/ui/views/widget/native_widget_private.h @@ -8,15 +8,16 @@ #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 FontList; class ImageSkia; class Rect; } namespace ui { +class InputMethod; class NativeTheme; class OSExchangeData; } @@ -25,6 +26,7 @@ namespace views { class InputMethod; class TooltipManager; namespace internal { +class InputMethodDelegate; //////////////////////////////////////////////////////////////////////////////// // NativeWidgetPrivate interface @@ -72,6 +74,8 @@ class VIEWS_EXPORT NativeWidgetPrivate : public NativeWidget { // Returns true if any touch device is currently down. static bool IsTouchDown(); + static gfx::FontList GetWindowTitleFontList(); + // Initializes the NativeWidget. virtual void InitNativeWidget(const Widget::InitParams& params) = 0; @@ -80,6 +84,7 @@ class VIEWS_EXPORT NativeWidgetPrivate : public NativeWidget { virtual NonClientFrameView* CreateNonClientFrameView() = 0; virtual bool ShouldUseNativeFrame() const = 0; + virtual bool ShouldWindowContentsBeTransparent() const = 0; virtual void FrameTypeChanged() = 0; // Returns the Widget associated with this NativeWidget. This function is @@ -141,6 +146,10 @@ class VIEWS_EXPORT NativeWidgetPrivate : public NativeWidget { // Returns the InputMethodDelegate for this native widget. virtual InputMethodDelegate* GetInputMethodDelegate() = 0; + // Returns the ui::InputMethod for this native widget. + // TODO(yukishiino): Rename this method to GetInputMethod once we remove + // views::InputMethod. + virtual ui::InputMethod* GetHostInputMethod() = 0; // Centers the window and sizes it to the specified size. virtual void CenterWindow(const gfx::Size& size) = 0; @@ -152,7 +161,7 @@ class VIEWS_EXPORT NativeWidgetPrivate : public NativeWidget { ui::WindowShowState* show_state) const = 0; // Sets the NativeWindow title. Returns true if the title changed. - virtual bool SetWindowTitle(const string16& title) = 0; + virtual bool SetWindowTitle(const base::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 @@ -189,6 +198,7 @@ class VIEWS_EXPORT NativeWidgetPrivate : public NativeWidget { virtual bool IsActive() const = 0; virtual void SetAlwaysOnTop(bool always_on_top) = 0; virtual bool IsAlwaysOnTop() const = 0; + virtual void SetVisibleOnAllWorkspaces(bool always_visible) = 0; virtual void Maximize() = 0; virtual void Minimize() = 0; virtual bool IsMaximized() const = 0; @@ -217,10 +227,13 @@ class VIEWS_EXPORT NativeWidgetPrivate : public NativeWidget { virtual void SetVisibilityChangedAnimationsEnabled(bool value) = 0; virtual ui::NativeTheme* GetNativeTheme() const = 0; virtual void OnRootViewLayout() const = 0; + virtual bool IsTranslucentWindowOpacitySupported() const = 0; + + // Repost an unhandled event to the native widget for default OS processing. + virtual void RepostNativeEvent(gfx::NativeEvent native_event) = 0; // Overridden from NativeWidget: virtual internal::NativeWidgetPrivate* AsNativeWidgetPrivate() OVERRIDE; - virtual ui::EventHandler* GetEventHandler() = 0; }; } // namespace internal diff --git a/chromium/ui/views/widget/native_widget_unittest.cc b/chromium/ui/views/widget/native_widget_unittest.cc index 133f8be04a5..9002e9da8f1 100644 --- a/chromium/ui/views/widget/native_widget_unittest.cc +++ b/chromium/ui/views/widget/native_widget_unittest.cc @@ -42,7 +42,6 @@ class NativeWidgetTest : public ViewsTestBase { 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(); diff --git a/chromium/ui/views/widget/native_widget_win.cc b/chromium/ui/views/widget/native_widget_win.cc deleted file mode 100644 index 3f992e8d894..00000000000 --- a/chromium/ui/views/widget/native_widget_win.cc +++ /dev/null @@ -1,1068 +0,0 @@ -// 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/ime/input_method_factory.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/mouse_wheel_util.h" -#include "ui/base/win/shell.h" -#include "ui/events/event.h" -#include "ui/events/keycodes/keyboard_code_conversion_win.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/gfx/win/dpi.h" -#include "ui/gfx/win/hwnd_util.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() and -// NativeWidget::GetAllOwnedWidgets. Adds any HWNDs that correspond to -// Widgets to a set. -BOOL CALLBACK EnumerateNativeWidgets(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 = gfx::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 = gfx::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 = gfx::win::ScreenToDIPRect(*bounds); -} - -bool NativeWidgetWin::SetWindowTitle(const string16& title) { - return 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 gfx::win::ScreenToDIPRect(bounds_in_pixels); -} - -gfx::Rect NativeWidgetWin::GetClientAreaBoundsInScreen() const { - gfx::Rect bounds_in_pixels = message_handler_->GetClientAreaBoundsInScreen(); - return gfx::win::ScreenToDIPRect(bounds_in_pixels); -} - -gfx::Rect NativeWidgetWin::GetRestoredBounds() const { - gfx::Rect bounds_in_pixels = message_handler_->GetRestoredBounds(); - return gfx::win::ScreenToDIPRect(bounds_in_pixels); -} - -void NativeWidgetWin::SetBounds(const gfx::Rect& bounds) { - float scale = gfx::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 = gfx::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); -} - -bool NativeWidgetWin::IsAlwaysOnTop() const { - return message_handler_->IsAlwaysOnTop(); -} - -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 = gfx::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 gfx::win::ScreenToDIPRect( - gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow( - GetNativeView()).work_area()); -} - -Widget::MoveLoopResult NativeWidgetWin::RunMoveLoop( - const gfx::Vector2d& drag_offset, - Widget::MoveLoopSource source, - Widget::MoveLoopEscapeBehavior escape_behavior) { - const bool hide_on_escape = - escape_behavior == Widget::MOVE_LOOP_ESCAPE_BEHAVIOR_HIDE; - return message_handler_->RunMoveLoop(drag_offset, hide_on_escape) ? - 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(); -} - -void NativeWidgetWin::OnRootViewLayout() const { -} - -//////////////////////////////////////////////////////////////////////////////// -// 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 = gfx::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 = gfx::win::ScreenToDIPSize(delegate_->GetMinimumSize()); - *max_size = gfx::win::ScreenToDIPSize(delegate_->GetMaximumSize()); -} - -gfx::Size NativeWidgetWin::GetRootViewSize() const { - gfx::Size pixel_size = GetWidget()->GetRootView()->size(); - return gfx::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::HandleVisibilityChanging(bool visible) { - delegate_->OnNativeWidgetVisibilityChanging(visible); -} - -void NativeWidgetWin::HandleVisibilityChanged(bool visible) { - delegate_->OnNativeWidgetVisibilityChanged(visible); -} - -void NativeWidgetWin::HandleClientSizeChanged(const gfx::Size& new_size) { - gfx::Size size_in_dip = gfx::win::ScreenToDIPSize(new_size); - delegate_->OnNativeWidgetSizeChanged(size_in_dip); -} - -void NativeWidgetWin::HandleFrameChanged() { - // Replace the frame and layout the contents. - GetWidget()->non_client_view()->UpdateFrame(); -} - -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/gfx::win::GetDeviceScaleFactor(), 0.0, - 0.0, 1/gfx::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()) { - 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; -} - -void NativeWidgetWin::HandleTouchEvent(const ui::TouchEvent& event) { - NOTREACHED() << "Touch events are not supported"; -} - -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 = gfx::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) { -} - -bool NativeWidgetWin::HandleScrollEvent(const ui::ScrollEvent& event) { - delegate_->OnScrollEvent(const_cast<ui::ScrollEvent*>(&event)); - return event.handled(); -} - -//////////////////////////////////////////////////////////////////////////////// -// 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 = gfx::win::DIPToScreenRect(*rect).ToRECT(); - if (::MapWindowPoints(source_hwnd, target_hwnd, - reinterpret_cast<LPPOINT>(&win_rect), - sizeof(RECT)/sizeof(POINT))) { - *rect = gfx::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, EnumerateNativeWidgets, - reinterpret_cast<LPARAM>(children)); -} - -// static -void NativeWidgetPrivate::GetAllOwnedWidgets(gfx::NativeView native_view, - Widget::Widgets* owned) { - if (!native_view) - return; - - Widget::Widgets all; - EnumWindows(EnumerateNativeWidgets, reinterpret_cast<LPARAM>(&all)); - for (Widget::Widgets::const_iterator iter = all.begin(); - iter != all.end(); ++iter) { - if (native_view == GetWindow((*iter)->GetNativeView(), GW_OWNER)) - owned->insert(*iter); - } -} - -// 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) { - (*it)->NotifyNativeViewHierarchyWillChange(); - } - - ::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(); - } -} - -// 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 deleted file mode 100644 index 31bee8b8828..00000000000 --- a/chromium/ui/views/widget/native_widget_win.h +++ /dev/null @@ -1,278 +0,0 @@ -// 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/gfx/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 bool 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 bool IsAlwaysOnTop() const 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 Widget::MoveLoopResult RunMoveLoop( - const gfx::Vector2d& drag_offset, - Widget::MoveLoopSource source, - Widget::MoveLoopEscapeBehavior escape_behavior) OVERRIDE; - virtual void EndMoveLoop() OVERRIDE; - virtual void SetVisibilityChangedAnimationsEnabled(bool value) OVERRIDE; - virtual ui::NativeTheme* GetNativeTheme() const OVERRIDE; - virtual void OnRootViewLayout() 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 HandleVisibilityChanging(bool visible) 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 void 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; - virtual bool HandleScrollEvent(const ui::ScrollEvent& event) 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 deleted file mode 100644 index 6a84ca06d70..00000000000 --- a/chromium/ui/views/widget/native_widget_win_unittest.cc +++ /dev/null @@ -1,83 +0,0 @@ -// 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 index c825998d914..b41e16cb1b9 100644 --- a/chromium/ui/views/widget/root_view.cc +++ b/chromium/ui/views/widget/root_view.cc @@ -8,22 +8,23 @@ #include "base/logging.h" #include "base/message_loop/message_loop.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" +#include "ui/base/cursor/cursor.h" #include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/ui_base_switches_util.h" #include "ui/compositor/layer.h" #include "ui/events/event.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/canvas.h" +#include "ui/views/drag_controller.h" #include "ui/views/focus/view_storage.h" #include "ui/views/layout/fill_layout.h" +#include "ui/views/view_targeter.h" #include "ui/views/views_switches.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" -#include "ui/views/widget/widget_deletion_observer.h" -#if defined(USE_AURA) -#include "ui/base/cursor/cursor.h" -#endif +typedef ui::EventDispatchDetails DispatchDetails; namespace views { namespace internal { @@ -51,6 +52,95 @@ class MouseEnterExitEvent : public ui::MouseEvent { } // namespace +// This event handler receives events in the pre-target phase and takes care of +// the following: +// - Shows keyboard-triggered context menus. +class PreEventDispatchHandler : public ui::EventHandler { + public: + explicit PreEventDispatchHandler(View* owner) + : owner_(owner) { + } + virtual ~PreEventDispatchHandler() {} + + private: + // ui::EventHandler: + virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE { + CHECK_EQ(ui::EP_PRETARGET, event->phase()); + if (event->handled()) + return; + + View* v = NULL; + if (owner_->GetFocusManager()) // Can be NULL in unittests. + v = owner_->GetFocusManager()->GetFocusedView(); + + // Special case to handle keyboard-triggered context menus. + if (v && v->enabled() && ((event->key_code() == ui::VKEY_APPS) || + (event->key_code() == ui::VKEY_F10 && event->IsShiftDown()))) { + // Clamp the menu location within the visible bounds of each ancestor view + // to avoid showing the menu over a completely different view or window. + gfx::Point location = v->GetKeyboardContextMenuLocation(); + for (View* parent = v->parent(); parent; parent = parent->parent()) { + const gfx::Rect& parent_bounds = parent->GetBoundsInScreen(); + location.SetToMax(parent_bounds.origin()); + location.SetToMin(parent_bounds.bottom_right()); + } + v->ShowContextMenu(location, ui::MENU_SOURCE_KEYBOARD); + event->StopPropagation(); + } + } + + View* owner_; + + DISALLOW_COPY_AND_ASSIGN(PreEventDispatchHandler); +}; + +// This event handler receives events in the post-target phase and takes care of +// the following: +// - Generates context menu, or initiates drag-and-drop, from gesture events. +class PostEventDispatchHandler : public ui::EventHandler { + public: + PostEventDispatchHandler() + : touch_dnd_enabled_(::switches::IsTouchDragDropEnabled()) { + } + virtual ~PostEventDispatchHandler() {} + + private: + // Overridden from ui::EventHandler: + virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { + DCHECK_EQ(ui::EP_POSTTARGET, event->phase()); + if (event->handled()) + return; + + View* target = static_cast<View*>(event->target()); + gfx::Point location = event->location(); + if (touch_dnd_enabled_ && + event->type() == ui::ET_GESTURE_LONG_PRESS && + (!target->drag_controller() || + target->drag_controller()->CanStartDragForView( + target, location, location))) { + if (target->DoDrag(*event, location, + ui::DragDropTypes::DRAG_EVENT_SOURCE_TOUCH)) { + event->StopPropagation(); + return; + } + } + + if (target->context_menu_controller() && + (event->type() == ui::ET_GESTURE_LONG_PRESS || + event->type() == ui::ET_GESTURE_LONG_TAP || + event->type() == ui::ET_GESTURE_TWO_FINGER_TAP)) { + gfx::Point screen_location(location); + View::ConvertPointToScreen(target, &screen_location); + target->ShowContextMenu(screen_location, ui::MENU_SOURCE_TOUCH); + event->StopPropagation(); + } + } + + bool touch_dnd_enabled_; + + DISALLOW_COPY_AND_ASSIGN(PostEventDispatchHandler); +}; + // static const char RootView::kViewClassName[] = "RootView"; @@ -68,13 +158,18 @@ RootView::RootView(Widget* widget) 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), + pre_dispatch_handler_(new internal::PreEventDispatchHandler(this)), + post_dispatch_handler_(new internal::PostEventDispatchHandler), focus_search_(this, false, false), focus_traversable_parent_(NULL), focus_traversable_parent_view_(NULL), - event_dispatch_target_(NULL) { + event_dispatch_target_(NULL), + old_dispatch_target_(NULL) { + AddPreTargetHandler(pre_dispatch_handler_.get()); + AddPostTargetHandler(post_dispatch_handler_.get()); + SetEventTargeter(scoped_ptr<ui::EventTargeter>(new ViewTargeter())); } RootView::~RootView() { @@ -111,232 +206,6 @@ void RootView::NotifyNativeViewHierarchyChanged() { PropagateNativeViewHierarchyChanged(); } -// 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()))) { - // Showing the context menu outside the visible bounds may result in a - // context menu appearing over a completely different window. Constrain - // location to visible bounds so this doesn't happen. - gfx::Rect visible_bounds(v->ConvertRectToWidget(v->GetVisibleBounds())); - visible_bounds.Offset( - widget_->GetClientAreaBoundsInScreen().OffsetFromOrigin()); - gfx::Rect keyboard_loc(v->GetKeyboardContextMenuLocation(), - gfx::Size(1, 1)); - keyboard_loc.AdjustToFit(visible_bounds); - v->ShowContextMenu(keyboard_loc.origin(), ui::MENU_SOURCE_KEYBOARD); - event->StopPropagation(); - return; - } - - DispatchKeyEventStartAt(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; - } - - View* gesture_handler = NULL; - if (views::switches::IsRectBasedTargetingEnabled() && - !event->details().bounding_box().IsEmpty()) { - // TODO(tdanderson): Pass in the bounding box to GetEventHandlerForRect() - // once crbug.com/313392 is resolved. - gfx::Rect touch_rect(event->details().bounding_box()); - touch_rect.set_origin(event->location()); - touch_rect.Offset(-touch_rect.width() / 2, -touch_rect.height() / 2); - gesture_handler = GetEventHandlerForRect(touch_rect); - } else { - gesture_handler = GetEventHandlerForPoint(event->location()); - } - - // Walk up the tree until we find a view that wants the gesture event. - for (gesture_handler_ = gesture_handler; - 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) { @@ -374,6 +243,35 @@ View* RootView::GetFocusTraversableParentView() { } //////////////////////////////////////////////////////////////////////////////// +// RootView, ui::EventProcessor overrides: + +ui::EventTarget* RootView::GetRootTarget() { + return this; +} + +ui::EventDispatchDetails RootView::OnEventFromSource(ui::Event* event) { + // TODO(tdanderson): Replace the calls to Dispatch*Event() with calls to + // EventProcessor::OnEventFromSource() once the code for + // that event type has been refactored, and then + // eventually remove this function altogether. See + // crbug.com/348083. + if (event->IsKeyEvent()) + return EventProcessor::OnEventFromSource(event); + else if (event->IsScrollEvent()) + return EventProcessor::OnEventFromSource(event); + else if (event->IsTouchEvent()) + NOTREACHED() << "Touch events should not be sent to RootView."; + else if (event->IsGestureEvent()) + DispatchGestureEvent(static_cast<ui::GestureEvent*>(event)); + else if (event->IsMouseEvent()) + NOTREACHED() << "Should not be called with a MouseEvent."; + else + NOTREACHED() << "Invalid event type."; + + return DispatchDetails(); +} + +//////////////////////////////////////////////////////////////////////////////// // RootView, View overrides: const Widget* RootView::GetWidget() const { @@ -419,7 +317,10 @@ bool RootView::OnMousePressed(const ui::MouseEvent& event) { ui::MouseEvent mouse_pressed_event(event, static_cast<View*>(this), mouse_pressed_handler_); drag_info_.Reset(); - DispatchEventToTarget(mouse_pressed_handler_, &mouse_pressed_event); + ui::EventDispatchDetails dispatch_details = + DispatchEvent(mouse_pressed_handler_, &mouse_pressed_event); + if (dispatch_details.dispatcher_destroyed) + return true; return true; } DCHECK(!explicit_mouse_handler_); @@ -447,12 +348,10 @@ bool RootView::OnMousePressed(const ui::MouseEvent& event) { 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(); - } + ui::EventDispatchDetails dispatch_details = + DispatchEvent(mouse_pressed_handler_, &mouse_pressed_event); + if (dispatch_details.dispatcher_destroyed) + 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 @@ -495,7 +394,10 @@ bool RootView::OnMouseDragged(const ui::MouseEvent& event) { ui::MouseEvent mouse_event(event, static_cast<View*>(this), mouse_pressed_handler_); - DispatchEventToTarget(mouse_pressed_handler_, &mouse_event); + ui::EventDispatchDetails dispatch_details = + DispatchEvent(mouse_pressed_handler_, &mouse_event); + if (dispatch_details.dispatcher_destroyed) + return false; } return false; } @@ -510,8 +412,10 @@ void RootView::OnMouseReleased(const ui::MouseEvent& event) { // 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. + ui::EventDispatchDetails dispatch_details = + DispatchEvent(mouse_pressed_handler, &mouse_released); + if (dispatch_details.dispatcher_destroyed) + return; } } @@ -524,7 +428,8 @@ void RootView::OnMouseCaptureLost() { 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_); + last_mouse_event_flags_, + 0); UpdateCursor(release_event); } // We allow the view to delete us from OnMouseCaptureLost. As such, @@ -554,7 +459,12 @@ void RootView::OnMouseMoved(const ui::MouseEvent& event) { (!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); + exit.ConvertLocationToTarget(static_cast<View*>(this), + mouse_move_handler_); + ui::EventDispatchDetails dispatch_details = + DispatchEvent(mouse_move_handler_, &exit); + if (dispatch_details.dispatcher_destroyed) + return; NotifyEnterExitOfDescendant(event, ui::ET_MOUSE_EXITED, mouse_move_handler_, v); } @@ -565,19 +475,28 @@ void RootView::OnMouseMoved(const ui::MouseEvent& event) { 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::EventDispatchDetails dispatch_details = + DispatchEvent(mouse_move_handler_, &entered); + if (dispatch_details.dispatcher_destroyed) + return; + NotifyEnterExitOfDescendant(event, ui::ET_MOUSE_ENTERED, + mouse_move_handler_, old_handler); } } ui::MouseEvent moved_event(event, static_cast<View*>(this), mouse_move_handler_); mouse_move_handler_->OnMouseMoved(moved_event); + // TODO(tdanderson): It may be possible to avoid setting the cursor twice + // (once here and once from CompoundEventFilter) on a + // mousemove. See crbug.com/351469. 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); + ui::EventDispatchDetails dispatch_details = + DispatchEvent(mouse_move_handler_, &exited); + if (dispatch_details.dispatcher_destroyed) + return; NotifyEnterExitOfDescendant(event, ui::ET_MOUSE_EXITED, mouse_move_handler_, v); // On Aura the non-client area extends slightly outside the root view for @@ -592,7 +511,10 @@ void RootView::OnMouseMoved(const ui::MouseEvent& event) { void RootView::OnMouseExited(const ui::MouseEvent& event) { if (mouse_move_handler_ != NULL) { MouseEnterExitEvent exited(event, ui::ET_MOUSE_EXITED); - DispatchEventToTarget(mouse_move_handler_, &exited); + ui::EventDispatchDetails dispatch_details = + DispatchEvent(mouse_move_handler_, &exited); + if (dispatch_details.dispatcher_destroyed) + return; NotifyEnterExitOfDescendant(event, ui::ET_MOUSE_EXITED, mouse_move_handler_, NULL); mouse_move_handler_ = NULL; @@ -601,8 +523,14 @@ void RootView::OnMouseExited(const ui::MouseEvent& event) { 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)); + v && v != this && !event.handled(); v = v->parent()) { + ui::EventDispatchDetails dispatch_details = + DispatchEvent(v, const_cast<ui::MouseWheelEvent*>(&event)); + if (dispatch_details.dispatcher_destroyed || + dispatch_details.target_destroyed) { + return event.handled(); + } + } return event.handled(); } @@ -615,7 +543,7 @@ void RootView::SetMouseHandler(View* new_mh) { drag_info_.Reset(); } -void RootView::GetAccessibleState(ui::AccessibleViewState* state) { +void RootView::GetAccessibleState(ui::AXViewState* state) { state->name = widget_->widget_delegate()->GetAccessibleWindowTitle(); state->role = widget_->widget_delegate()->GetAccessibleWindowRole(); } @@ -628,6 +556,142 @@ void RootView::UpdateParentLayer() { //////////////////////////////////////////////////////////////////////////////// // RootView, protected: +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); + ui::EventDispatchDetails dispatch_details = + DispatchEvent(handler, &handler_event); + if (dispatch_details.dispatcher_destroyed) + return; + + 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_); + ui::EventDispatchDetails dispatch_details = + DispatchEvent(scroll_gesture_handler_, &gesture_event); + if (gesture_event.stopped_propagation()) { + event->StopPropagation(); + return; + } else if (gesture_event.handled()) { + event->SetHandled(); + return; + } else if (dispatch_details.dispatcher_destroyed || + dispatch_details.target_destroyed) { + 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; + } + + View* gesture_handler = NULL; + if (views::switches::IsRectBasedTargetingEnabled() && + !event->details().bounding_box().IsEmpty()) { + // TODO(tdanderson): Pass in the bounding box to GetEventHandlerForRect() + // once crbug.com/313392 is resolved. + gfx::Rect touch_rect(event->details().bounding_box()); + touch_rect.set_origin(event->location()); + touch_rect.Offset(-touch_rect.width() / 2, -touch_rect.height() / 2); + gesture_handler = GetEventHandlerForRect(touch_rect); + } else { + gesture_handler = GetEventHandlerForPoint(event->location()); + } + + // Walk up the tree until we find a view that wants the gesture event. + for (gesture_handler_ = gesture_handler; + 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_); + ui::EventDispatchDetails dispatch_details = + DispatchEvent(gesture_handler_, &gesture_event); + if (dispatch_details.dispatcher_destroyed) + return; + + // 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(); + // Last ui::ET_GESTURE_END should not set the gesture_handler_. + if (gesture_event.type() == ui::ET_GESTURE_END && + event->details().touch_points() <= 1) { + gesture_handler_ = NULL; + } + return; + } + + // The gesture event wasn't processed. Go up the view hierarchy and + // dispatch the gesture event. + } + + gesture_handler_ = NULL; +} + void RootView::ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) { widget_->ViewHierarchyChanged(details); @@ -637,14 +701,14 @@ void RootView::ViewHierarchyChanged( 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; + if (old_dispatch_target_ == details.child) + old_dispatch_target_ = NULL; } } @@ -656,10 +720,10 @@ void RootView::VisibilityChanged(View* /*starting_from*/, bool is_visible) { explicit_mouse_handler_ = false; mouse_pressed_handler_ = NULL; mouse_move_handler_ = NULL; - touch_pressed_handler_ = NULL; gesture_handler_ = NULL; scroll_gesture_handler_ = NULL; event_dispatch_target_ = NULL; + old_dispatch_target_ = NULL; } } @@ -701,14 +765,6 @@ void RootView::SetMouseLocationAndFlags(const ui::MouseEvent& event) { last_mouse_event_y_ = event.y(); } -void RootView::DispatchEventToTarget(View* target, ui::Event* event) { - View* old_target = event_dispatch_target_; - event_dispatch_target_ = target; - ui::EventDispatchDetails details = DispatchEvent(target, event); - if (!details.dispatcher_destroyed) - event_dispatch_target_ = old_target; -} - void RootView::NotifyEnterExitOfDescendant(const ui::MouseEvent& event, ui::EventType type, View* view, @@ -722,20 +778,11 @@ void RootView::NotifyEnterExitOfDescendant(const ui::MouseEvent& event, // 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); - } -} - - -void RootView::DispatchKeyEventStartAt(View* view, ui::KeyEvent* event) { - if (event->handled() || !view) - return; - - for (; view && view != this; view = view->parent()) { - DispatchEventToTarget(view, event); - // Do this check here rather than in the if as |view| may have been deleted. - if (event->handled()) + ui::EventDispatchDetails dispatch_details = DispatchEvent(p, ¬ify_event); + if (dispatch_details.dispatcher_destroyed || + dispatch_details.target_destroyed) { return; + } } } @@ -743,5 +790,28 @@ bool RootView::CanDispatchToTarget(ui::EventTarget* target) { return event_dispatch_target_ == target; } +ui::EventDispatchDetails RootView::PreDispatchEvent(ui::EventTarget* target, + ui::Event* event) { + old_dispatch_target_ = event_dispatch_target_; + event_dispatch_target_ = static_cast<View*>(target); + return DispatchDetails(); +} + +ui::EventDispatchDetails RootView::PostDispatchEvent(ui::EventTarget* target, + const ui::Event& event) { + DispatchDetails details; + if (target != event_dispatch_target_) + details.target_destroyed = true; + + event_dispatch_target_ = old_dispatch_target_; + old_dispatch_target_ = NULL; + +#ifndef NDEBUG + DCHECK(!event_dispatch_target_ || Contains(event_dispatch_target_)); +#endif + + return details; +} + } // namespace internal } // namespace views diff --git a/chromium/ui/views/widget/root_view.h b/chromium/ui/views/widget/root_view.h index 6ab367bd992..75788234b9b 100644 --- a/chromium/ui/views/widget/root_view.h +++ b/chromium/ui/views/widget/root_view.h @@ -8,7 +8,7 @@ #include <string> #include "base/memory/ref_counted.h" -#include "ui/events/event_dispatcher.h" +#include "ui/events/event_processor.h" #include "ui/views/focus/focus_manager.h" #include "ui/views/focus/focus_search.h" #include "ui/views/view.h" @@ -16,7 +16,6 @@ namespace views { namespace test { -class RootViewTestHelper; class WidgetTest; } @@ -25,6 +24,7 @@ class Widget; // This is a views-internal API and should not be used externally. // Widget exposes this object as a View*. namespace internal { +class PreEventDispatchHandler; //////////////////////////////////////////////////////////////////////////////// // RootView class @@ -45,7 +45,7 @@ namespace internal { // class VIEWS_EXPORT RootView : public View, public FocusTraversable, - public ui::EventDispatcherDelegate { + public ui::EventProcessor { public: static const char kViewClassName[]; @@ -63,16 +63,6 @@ class VIEWS_EXPORT RootView : public View, // Called when parent of the host changed. void NotifyNativeViewHierarchyChanged(); - // 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 @@ -97,6 +87,10 @@ class VIEWS_EXPORT RootView : public View, virtual FocusTraversable* GetFocusTraversableParent() OVERRIDE; virtual View* GetFocusTraversableParentView() OVERRIDE; + // Overridden from ui::EventProcessor: + virtual ui::EventTarget* GetRootTarget() OVERRIDE; + virtual ui::EventDispatchDetails OnEventFromSource(ui::Event* event) OVERRIDE; + // Overridden from View: virtual const Widget* GetWidget() const OVERRIDE; virtual Widget* GetWidget() OVERRIDE; @@ -112,10 +106,15 @@ class VIEWS_EXPORT RootView : public View, 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 GetAccessibleState(ui::AXViewState* state) OVERRIDE; virtual void UpdateParentLayer() OVERRIDE; protected: + // TODO(tdanderson): Remove RootView::DispatchGestureEvent() once + // its targeting and dispatch logic has been moved + // elsewhere. See crbug.com/348083. + virtual void DispatchGestureEvent(ui::GestureEvent* event); + // Overridden from View: virtual void ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) OVERRIDE; @@ -128,7 +127,6 @@ class VIEWS_EXPORT RootView : public View, private: friend class ::views::View; friend class ::views::Widget; - friend class ::views::test::RootViewTestHelper; friend class ::views::test::WidgetTest; // Input --------------------------------------------------------------------- @@ -145,8 +143,6 @@ class VIEWS_EXPORT RootView : public View, // 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|. @@ -155,15 +151,14 @@ class VIEWS_EXPORT RootView : public View, View* view, View* sibling); - // Dispatches the KeyEvent to |view| and ancestors until the event is - // handled. - void DispatchKeyEventStartAt(View* view, ui::KeyEvent* event); - // Overridden from ui::EventDispatcherDelegate: virtual bool CanDispatchToTarget(ui::EventTarget* target) OVERRIDE; + virtual ui::EventDispatchDetails PreDispatchEvent(ui::EventTarget* target, + ui::Event* event) OVERRIDE; + virtual ui::EventDispatchDetails PostDispatchEvent( + ui::EventTarget* target, const ui::Event& event) OVERRIDE; ////////////////////////////////////////////////////////////////////////////// - // Tree operations ----------------------------------------------------------- // The host Widget @@ -190,9 +185,6 @@ class VIEWS_EXPORT RootView : public View, 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). @@ -201,6 +193,9 @@ class VIEWS_EXPORT RootView : public View, // The view currently handling scroll gesture events. View* scroll_gesture_handler_; + scoped_ptr<internal::PreEventDispatchHandler> pre_dispatch_handler_; + scoped_ptr<internal::PostEventDispatchHandler> post_dispatch_handler_; + // Focus --------------------------------------------------------------------- // The focus search algorithm. @@ -217,6 +212,7 @@ class VIEWS_EXPORT RootView : public View, View* focus_traversable_parent_view_; View* event_dispatch_target_; + View* old_dispatch_target_; // Drag and drop ------------------------------------------------------------- diff --git a/chromium/ui/views/widget/root_view_test_helper.h b/chromium/ui/views/widget/root_view_test_helper.h deleted file mode 100644 index a26396730b2..00000000000 --- a/chromium/ui/views/widget/root_view_test_helper.h +++ /dev/null @@ -1,33 +0,0 @@ -// 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_ROOT_VIEW_TEST_HELPER_H_ -#define UI_VIEWS_WIDGET_ROOT_VIEW_TEST_HELPER_H_ - -#include "ui/views/widget/root_view.h" - -namespace views { -namespace test { - -class RootViewTestHelper { - public: - explicit RootViewTestHelper(internal::RootView* root_view) - : root_view_(root_view) { - } - ~RootViewTestHelper() {} - - void DispatchKeyEventStartAt(View* view, ui::KeyEvent* event) { - root_view_->DispatchKeyEventStartAt(view, event); - } - - private: - internal::RootView* root_view_; - - DISALLOW_COPY_AND_ASSIGN(RootViewTestHelper); -}; - -} // namespace test -} // namespace views - -#endif // UI_VIEWS_WIDGET_ROOT_VIEW_TEST_HELPER_H_ diff --git a/chromium/ui/views/widget/root_view_unittest.cc b/chromium/ui/views/widget/root_view_unittest.cc index ebe20d65346..9def7726fdd 100644 --- a/chromium/ui/views/widget/root_view_unittest.cc +++ b/chromium/ui/views/widget/root_view_unittest.cc @@ -2,8 +2,13 @@ // 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 "ui/events/event_targeter.h" +#include "ui/views/context_menu_controller.h" #include "ui/views/test/views_test_base.h" -#include "ui/views/widget/root_view_test_helper.h" +#include "ui/views/view_targeter.h" +#include "ui/views/widget/root_view.h" namespace views { namespace test { @@ -28,7 +33,8 @@ class DeleteOnKeyEventView : public View { DISALLOW_COPY_AND_ASSIGN(DeleteOnKeyEventView); }; -// Verifies deleting a View in OnKeyPressed() doesn't crash. +// Verifies deleting a View in OnKeyPressed() doesn't crash and that the +// target is marked as destroyed in the returned EventDispatchDetails. TEST_F(RootViewTest, DeleteViewDuringKeyEventDispatch) { Widget widget; Widget::InitParams init_params = @@ -44,12 +50,194 @@ TEST_F(RootViewTest, DeleteViewDuringKeyEventDispatch) { View* child = new DeleteOnKeyEventView(&got_key_event); content->AddChildView(child); + // Give focus to |child| so that it will be the target of the key event. + child->SetFocusable(true); + child->RequestFocus(); + + ui::EventTargeter* targeter = new ViewTargeter(); + internal::RootView* root_view = + static_cast<internal::RootView*>(widget.GetRootView()); + root_view->SetEventTargeter(make_scoped_ptr(targeter)); + ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_ESCAPE, 0, false); - RootViewTestHelper test_helper( - static_cast<internal::RootView*>(widget.GetRootView())); - test_helper.DispatchKeyEventStartAt(child, &key_event); + ui::EventDispatchDetails details = root_view->OnEventFromSource(&key_event); + EXPECT_TRUE(details.target_destroyed); + EXPECT_FALSE(details.dispatcher_destroyed); EXPECT_TRUE(got_key_event); } +// Tracks whether a context menu is shown. +class TestContextMenuController : public ContextMenuController { + public: + TestContextMenuController() + : show_context_menu_calls_(0), + menu_source_view_(NULL), + menu_source_type_(ui::MENU_SOURCE_NONE) { + } + virtual ~TestContextMenuController() {} + + int show_context_menu_calls() const { return show_context_menu_calls_; } + View* menu_source_view() const { return menu_source_view_; } + ui::MenuSourceType menu_source_type() const { return menu_source_type_; } + + void Reset() { + show_context_menu_calls_ = 0; + menu_source_view_ = NULL; + menu_source_type_ = ui::MENU_SOURCE_NONE; + } + + // ContextMenuController: + virtual void ShowContextMenuForView( + View* source, + const gfx::Point& point, + ui::MenuSourceType source_type) OVERRIDE { + show_context_menu_calls_++; + menu_source_view_ = source; + menu_source_type_ = source_type; + } + + private: + int show_context_menu_calls_; + View* menu_source_view_; + ui::MenuSourceType menu_source_type_; + + DISALLOW_COPY_AND_ASSIGN(TestContextMenuController); +}; + +// Tests that context menus are shown for certain key events (Shift+F10 +// and VKEY_APPS) by the pre-target handler installed on RootView. +TEST_F(RootViewTest, ContextMenuFromKeyEvent) { + Widget widget; + Widget::InitParams init_params = + CreateParams(Widget::InitParams::TYPE_POPUP); + init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget.Init(init_params); + internal::RootView* root_view = + static_cast<internal::RootView*>(widget.GetRootView()); + + TestContextMenuController controller; + View* focused_view = new View; + focused_view->set_context_menu_controller(&controller); + widget.SetContentsView(focused_view); + focused_view->SetFocusable(true); + focused_view->RequestFocus(); + + // No context menu should be shown for a keypress of 'A'. + ui::KeyEvent nomenu_key_event(ui::ET_KEY_PRESSED, ui::VKEY_A, 0, true); + ui::EventDispatchDetails details = + root_view->OnEventFromSource(&nomenu_key_event); + EXPECT_FALSE(details.target_destroyed); + EXPECT_FALSE(details.dispatcher_destroyed); + EXPECT_EQ(0, controller.show_context_menu_calls()); + EXPECT_EQ(NULL, controller.menu_source_view()); + EXPECT_EQ(ui::MENU_SOURCE_NONE, controller.menu_source_type()); + controller.Reset(); + + // A context menu should be shown for a keypress of Shift+F10. + ui::KeyEvent menu_key_event( + ui::ET_KEY_PRESSED, ui::VKEY_F10, ui::EF_SHIFT_DOWN, false); + details = root_view->OnEventFromSource(&menu_key_event); + EXPECT_FALSE(details.target_destroyed); + EXPECT_FALSE(details.dispatcher_destroyed); + EXPECT_EQ(1, controller.show_context_menu_calls()); + EXPECT_EQ(focused_view, controller.menu_source_view()); + EXPECT_EQ(ui::MENU_SOURCE_KEYBOARD, controller.menu_source_type()); + controller.Reset(); + + // A context menu should be shown for a keypress of VKEY_APPS. + ui::KeyEvent menu_key_event2(ui::ET_KEY_PRESSED, ui::VKEY_APPS, 0, false); + details = root_view->OnEventFromSource(&menu_key_event2); + EXPECT_FALSE(details.target_destroyed); + EXPECT_FALSE(details.dispatcher_destroyed); + EXPECT_EQ(1, controller.show_context_menu_calls()); + EXPECT_EQ(focused_view, controller.menu_source_view()); + EXPECT_EQ(ui::MENU_SOURCE_KEYBOARD, controller.menu_source_type()); + controller.Reset(); +} + +// View which handles all gesture events. +class GestureHandlingView : public View { + public: + GestureHandlingView() { + } + + virtual ~GestureHandlingView() { + } + + virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { + event->SetHandled(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(GestureHandlingView); +}; + +// Tests that context menus are shown for long press by the post-target handler +// installed on the RootView only if the event is targetted at a view which can +// show a context menu. +TEST_F(RootViewTest, ContextMenuFromLongPress) { + Widget widget; + Widget::InitParams init_params = + CreateParams(Widget::InitParams::TYPE_POPUP); + init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + init_params.bounds = gfx::Rect(100,100); + widget.Init(init_params); + internal::RootView* root_view = + static_cast<internal::RootView*>(widget.GetRootView()); + + // Create a view capable of showing the context menu with two children one of + // which handles all gesture events (e.g. a button). + TestContextMenuController controller; + View* parent_view = new View; + parent_view->set_context_menu_controller(&controller); + widget.SetContentsView(parent_view); + + View* gesture_handling_child_view = new GestureHandlingView; + gesture_handling_child_view->SetBoundsRect(gfx::Rect(10,10)); + parent_view->AddChildView(gesture_handling_child_view); + + View* other_child_view = new View; + other_child_view->SetBoundsRect(gfx::Rect(20, 0, 10,10)); + parent_view->AddChildView(other_child_view); + + // |parent_view| should not show a context menu as a result of a long press on + // |gesture_handling_child_view|. + ui::GestureEvent begin1(ui::ET_GESTURE_BEGIN, 5, 5, 0, base::TimeDelta(), + ui::GestureEventDetails(ui::ET_GESTURE_BEGIN, 0, 0), 1); + ui::EventDispatchDetails details = root_view->OnEventFromSource(&begin1); + + ui::GestureEvent long_press1(ui::ET_GESTURE_LONG_PRESS, 5, 5, 0, + base::TimeDelta(), + ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS, 0, 0), 1); + details = root_view->OnEventFromSource(&long_press1); + + ui::GestureEvent end1(ui::ET_GESTURE_END, 5, 5, 0, base::TimeDelta(), + ui::GestureEventDetails(ui::ET_GESTURE_END, 0, 0), 1); + details = root_view->OnEventFromSource(&end1); + + EXPECT_FALSE(details.target_destroyed); + EXPECT_FALSE(details.dispatcher_destroyed); + EXPECT_EQ(0, controller.show_context_menu_calls()); + + // |parent_view| should show a context menu as a result of a long press on + // |other_child_view|. + ui::GestureEvent begin2(ui::ET_GESTURE_BEGIN, 25, 5, 0, base::TimeDelta(), + ui::GestureEventDetails(ui::ET_GESTURE_BEGIN, 0, 0), 1); + details = root_view->OnEventFromSource(&begin2); + + ui::GestureEvent long_press2(ui::ET_GESTURE_LONG_PRESS, 25, 5, 0, + base::TimeDelta(), + ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS, 0, 0), 1); + details = root_view->OnEventFromSource(&long_press2); + + ui::GestureEvent end2(ui::ET_GESTURE_END, 25, 5, 0, base::TimeDelta(), + ui::GestureEventDetails(ui::ET_GESTURE_END, 0, 0), 1); + details = root_view->OnEventFromSource(&end2); + + EXPECT_FALSE(details.target_destroyed); + EXPECT_FALSE(details.dispatcher_destroyed); + EXPECT_EQ(1, controller.show_context_menu_calls()); +} + } // namespace test } // namespace views diff --git a/chromium/ui/views/widget/tooltip_manager_aura.cc b/chromium/ui/views/widget/tooltip_manager_aura.cc index 173597939c9..99ae9e6be57 100644 --- a/chromium/ui/views/widget/tooltip_manager_aura.cc +++ b/chromium/ui/views/widget/tooltip_manager_aura.cc @@ -6,12 +6,13 @@ #include "base/logging.h" #include "ui/aura/client/screen_position_client.h" -#include "ui/aura/client/tooltip_client.h" -#include "ui/aura/root_window.h" +#include "ui/aura/window_event_dispatcher.h" +#include "ui/aura/window_tree_host.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/rect.h" #include "ui/gfx/screen.h" #include "ui/views/widget/widget.h" +#include "ui/wm/public/tooltip_client.h" namespace views { @@ -49,7 +50,7 @@ void TooltipManagerAura::UpdateTooltipManagerForCapture(Widget* source) { return; gfx::Point screen_loc( - root_window->GetDispatcher()->GetLastMouseLocationInRoot()); + root_window->GetHost()->dispatcher()->GetLastMouseLocationInRoot()); aura::client::ScreenPositionClient* screen_position_client = aura::client::GetScreenPositionClient(root_window); if (!screen_position_client) @@ -91,7 +92,7 @@ void TooltipManagerAura::UpdateTooltip() { aura::Window* root_window = GetWindow()->GetRootWindow(); if (aura::client::GetTooltipClient(root_window)) { gfx::Point view_point = - root_window->GetDispatcher()->GetLastMouseLocationInRoot(); + root_window->GetHost()->dispatcher()->GetLastMouseLocationInRoot(); aura::Window::ConvertPointToTarget(root_window, GetWindow(), &view_point); View* view = GetViewUnderPoint(view_point); UpdateTooltipForTarget(view, view_point, root_window); @@ -102,7 +103,7 @@ void TooltipManagerAura::TooltipTextChanged(View* view) { aura::Window* root_window = GetWindow()->GetRootWindow(); if (aura::client::GetTooltipClient(root_window)) { gfx::Point view_point = - root_window->GetDispatcher()->GetLastMouseLocationInRoot(); + root_window->GetHost()->dispatcher()->GetLastMouseLocationInRoot(); aura::Window::ConvertPointToTarget(root_window, GetWindow(), &view_point); View* target = GetViewUnderPoint(view_point); if (target != view) @@ -124,7 +125,7 @@ void TooltipManagerAura::UpdateTooltipForTarget(View* target, if (target) { gfx::Point view_point = point; View::ConvertPointFromWidget(target, &view_point); - string16 new_tooltip_text; + base::string16 new_tooltip_text; if (!target->GetTooltipText(view_point, &new_tooltip_text)) tooltip_text_.clear(); else @@ -132,6 +133,9 @@ void TooltipManagerAura::UpdateTooltipForTarget(View* target, } else { tooltip_text_.clear(); } + + aura::client::SetTooltipId(GetWindow(), target); + aura::client::GetTooltipClient(root_window)->UpdateTooltip(GetWindow()); } diff --git a/chromium/ui/views/widget/tooltip_manager_aura.h b/chromium/ui/views/widget/tooltip_manager_aura.h index ed69c6d79b9..e723299060b 100644 --- a/chromium/ui/views/widget/tooltip_manager_aura.h +++ b/chromium/ui/views/widget/tooltip_manager_aura.h @@ -52,7 +52,7 @@ class TooltipManagerAura : public TooltipManager { aura::Window* GetWindow(); Widget* widget_; - string16 tooltip_text_; + base::string16 tooltip_text_; DISALLOW_COPY_AND_ASSIGN(TooltipManagerAura); }; diff --git a/chromium/ui/views/widget/tooltip_manager_win.cc b/chromium/ui/views/widget/tooltip_manager_win.cc deleted file mode 100644 index f9965f9cc37..00000000000 --- a/chromium/ui/views/widget/tooltip_manager_win.cc +++ /dev/null @@ -1,357 +0,0 @@ -// 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 <vector> - -#include "base/bind.h" -#include "base/i18n/rtl.h" -#include "base/logging.h" -#include "base/message_loop/message_loop.h" -#include "base/strings/string_split.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/gfx/font_list.h" -#include "ui/gfx/screen.h" -#include "ui/gfx/text_elider.h" -#include "ui/gfx/text_utils.h" -#include "ui/gfx/win/dpi.h" -#include "ui/gfx/win/hwnd_util.h" -#include "ui/gfx/win/scoped_set_map_mode.h" -#include "ui/views/view.h" -#include "ui/views/widget/monitor_win.h" -#include "ui/views/widget/widget.h" - -namespace views { - -namespace { - -static int tooltip_height_ = 0; - -// Maximum number of lines we allow in the tooltip. -const size_t kMaxLines = 6; - -// 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. |available_width| gives the space available -// for the tooltip. -void TrimTooltipToFit(const gfx::FontList& font_list, - int available_width, - base::string16* text, - int* max_width, - int* line_count) { - *max_width = 0; - *line_count = 0; - - TooltipManager::TrimTooltipText(text); - - // Split the string into at most kMaxLines lines. - std::vector<base::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. - base::string16 result; - for (std::vector<base::string16>::iterator i = lines.begin(); - i != lines.end(); ++i) { - base::string16 elided_text = - gfx::ElideText(*i, font_list, available_width, gfx::ELIDE_AT_END); - *max_width = std::max(*max_width, - gfx::GetStringWidth(elided_text, font_list)); - if (!result.empty()) - result.push_back('\n'); - result.append(elided_text); - } - *text = result; -} - -} // namespace - -// 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; -} - -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) { - DCHECK(widget); - DCHECK(widget->GetNativeView()); -} - -TooltipManagerWin::~TooltipManagerWin() { - if (tooltip_hwnd_) - DestroyWindow(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(); -} - -const gfx::FontList& TooltipManagerWin::GetFontList() const { - static gfx::FontList* font_list = NULL; - if (!font_list) - font_list = new gfx::FontList(DetermineDefaultFont()); - return *font_list; -} - -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_) - return 0; - - 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( - GetFontList(), - GetMaxWidth(screen_loc.x(), screen_loc.y(), - widget_->GetNativeView()), - &clipped_text_, &tooltip_width_, &line_count_); - // 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 = gfx::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); - gfx::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 = gfx::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 = gfx::win::ScreenToDIPPoint(mouse_pos_in_pixels); - } - - if (u_msg != WM_MOUSEMOVE || last_mouse_pos_ != mouse_pos) { - last_mouse_pos_ = mouse_pos; - 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); -} - -} // namespace views diff --git a/chromium/ui/views/widget/tooltip_manager_win.h b/chromium/ui/views/widget/tooltip_manager_win.h deleted file mode 100644 index e12dfbb2859..00000000000 --- a/chromium/ui/views/widget/tooltip_manager_win.h +++ /dev/null @@ -1,135 +0,0 @@ -// 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/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(); - - // TooltipManager: - virtual const gfx::FontList& TooltipManagerWin::GetFontList() const OVERRIDE; - virtual void UpdateTooltip() OVERRIDE; - virtual void TooltipTextChanged(View* view) 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(); - - // 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_; - - 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 index b050ae60ef4..3f4a1707fbc 100644 --- a/chromium/ui/views/widget/widget.cc +++ b/chromium/ui/views/widget/widget.cc @@ -8,6 +8,7 @@ #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/strings/utf_string_conversions.h" +#include "ui/base/cursor/cursor.h" #include "ui/base/default_theme_provider.h" #include "ui/base/hit_test.h" #include "ui/base/l10n/l10n_font_util.h" @@ -15,6 +16,7 @@ #include "ui/compositor/compositor.h" #include "ui/compositor/layer.h" #include "ui/events/event.h" +#include "ui/gfx/image/image_skia.h" #include "ui/gfx/screen.h" #include "ui/views/controls/menu/menu_controller.h" #include "ui/views/focus/focus_manager.h" @@ -29,13 +31,10 @@ #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/widget/widget_removals_observer.h" #include "ui/views/window/custom_frame_view.h" #include "ui/views/window/dialog_delegate.h" -#if defined(USE_AURA) -#include "ui/base/cursor/cursor.h" -#endif - namespace views { namespace { @@ -70,11 +69,7 @@ NativeWidget* CreateNativeWidget(NativeWidget* native_widget, // 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 && - params.type != Widget::InitParams::TYPE_DRAG) { + explicit DefaultWidgetDelegate(Widget* widget) : widget_(widget) { } virtual ~DefaultWidgetDelegate() {} @@ -88,9 +83,6 @@ class DefaultWidgetDelegate : public WidgetDelegate { virtual const Widget* GetWidget() const OVERRIDE { return widget_; } - virtual bool CanActivate() const OVERRIDE { - return can_activate_; - } virtual bool ShouldAdvanceFocusToTopLevelWidget() const OVERRIDE { // In most situations where a Widget is used without a delegate the Widget // is used as a container, so that we want focus to advance to the top-level @@ -100,7 +92,6 @@ class DefaultWidgetDelegate : public WidgetDelegate { private: Widget* widget_; - bool can_activate_; DISALLOW_COPY_AND_ASSIGN(DefaultWidgetDelegate); }; @@ -112,52 +103,48 @@ Widget::InitParams::InitParams() : type(TYPE_WINDOW), delegate(NULL), child(false), - opacity((ViewsDelegate::views_delegate && - ViewsDelegate::views_delegate->UseTransparentWindows()) ? - TRANSLUCENT_WINDOW : INFER_OPACITY), + opacity(INFER_OPACITY), accept_events(true), - can_activate(true), + activatable(ACTIVATABLE_DEFAULT), keep_on_top(false), + visible_on_all_workspaces(false), ownership(NATIVE_WIDGET_OWNS_WIDGET), mirror_origin_in_rtl(false), - has_dropshadow(false), + shadow_type(SHADOW_TYPE_DEFAULT), 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) { + desktop_window_tree_host(NULL), + layer_type(aura::WINDOW_LAYER_TEXTURED), + context(NULL), + force_show_in_taskbar(false) { } 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), + child(false), + opacity(INFER_OPACITY), accept_events(true), - can_activate(type != TYPE_POPUP && type != TYPE_MENU && - type != TYPE_DRAG), + activatable(ACTIVATABLE_DEFAULT), keep_on_top(type == TYPE_MENU || type == TYPE_DRAG), + visible_on_all_workspaces(false), ownership(NATIVE_WIDGET_OWNS_WIDGET), mirror_origin_in_rtl(false), - has_dropshadow(false), + shadow_type(SHADOW_TYPE_DEFAULT), 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) { + desktop_window_tree_host(NULL), + layer_type(aura::WINDOW_LAYER_TEXTURED), + context(NULL), + force_show_in_taskbar(false) { } Widget::InitParams::~InitParams() { @@ -186,7 +173,8 @@ Widget::Widget() last_mouse_event_was_move_(false), auto_release_capture_(true), root_layers_dirty_(false), - movement_disabled_(false) { + movement_disabled_(false), + observer_manager_(this) { } Widget::~Widget() { @@ -212,20 +200,19 @@ Widget* Widget::CreateWindowWithBounds(WidgetDelegate* delegate, 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) { + gfx::NativeView parent) { return CreateWindowWithParentAndBounds(delegate, parent, gfx::Rect()); } // static Widget* Widget::CreateWindowWithParentAndBounds(WidgetDelegate* delegate, - gfx::NativeWindow parent, + gfx::NativeView parent, const gfx::Rect& bounds) { Widget* widget = new Widget; Widget::InitParams params; @@ -256,24 +243,6 @@ Widget* Widget::CreateWindowWithContextAndBounds(WidgetDelegate* delegate, } // static -Widget* Widget::CreateWindowAsFramelessChild(WidgetDelegate* widget_delegate, - gfx::NativeView parent) { - views::Widget* widget = new views::Widget; - - views::Widget::InitParams params; - params.delegate = widget_delegate; - params.child = true; - params.parent = parent; - params.remove_standard_frame = true; -#if defined(USE_AURA) - params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; -#endif - - widget->Init(params); - return widget; -} - -// static Widget* Widget::GetWidgetForNativeView(gfx::NativeView native_view) { internal::NativeWidgetPrivate* native_widget = internal::NativeWidgetPrivate::GetNativeWidgetForNativeView(native_view); @@ -344,29 +313,39 @@ 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 - } + params.child |= (params.type == InitParams::TYPE_CONTROL); + is_top_level_ = !params.child; + + if (params.opacity == views::Widget::InitParams::INFER_OPACITY && + params.type != views::Widget::InitParams::TYPE_WINDOW && + params.type != views::Widget::InitParams::TYPE_PANEL) + params.opacity = views::Widget::InitParams::OPAQUE_WINDOW; if (ViewsDelegate::views_delegate) ViewsDelegate::views_delegate->OnBeforeWidgetInit(¶ms, this); + if (params.opacity == views::Widget::InitParams::INFER_OPACITY) + params.opacity = views::Widget::InitParams::OPAQUE_WINDOW; + + bool can_activate = false; + if (params.activatable != InitParams::ACTIVATABLE_DEFAULT) { + can_activate = (params.activatable == InitParams::ACTIVATABLE_YES); + } else if (params.type != InitParams::TYPE_CONTROL && + params.type != InitParams::TYPE_POPUP && + params.type != InitParams::TYPE_MENU && + params.type != InitParams::TYPE_TOOLTIP && + params.type != InitParams::TYPE_DRAG) { + can_activate = true; + params.activatable = InitParams::ACTIVATABLE_YES; + } else { + can_activate = false; + params.activatable = InitParams::ACTIVATABLE_NO; + } + widget_delegate_ = params.delegate ? - params.delegate : new DefaultWidgetDelegate(this, params); + params.delegate : new DefaultWidgetDelegate(this); + widget_delegate_->set_can_activate(can_activate); + ownership_ = params.ownership; native_widget_ = CreateNativeWidget(params.native_widget, this)-> AsNativeWidgetPrivate(); @@ -388,6 +367,7 @@ void Widget::Init(const InitParams& in_params) { // 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(); + non_client_view_->ResetWindowControls(); SetInitialBounds(params.bounds); if (params.show_state == ui::SHOW_STATE_MAXIMIZED) Maximize(); @@ -397,6 +377,9 @@ void Widget::Init(const InitParams& in_params) { SetContentsView(params.delegate->GetContentsView()); SetInitialBoundsForFramelessWindow(params.bounds); } + // This must come after SetContentsView() or it might not be able to find + // the correct NativeTheme (on Linux). See http://crbug.com/384492 + observer_manager_.Add(GetNativeTheme()); native_widget_initialized_ = true; } @@ -422,7 +405,19 @@ bool Widget::HasObserver(WidgetObserver* observer) { return observers_.HasObserver(observer); } -bool Widget::GetAccelerator(int cmd_id, ui::Accelerator* accelerator) { +void Widget::AddRemovalsObserver(WidgetRemovalsObserver* observer) { + removals_observers_.AddObserver(observer); +} + +void Widget::RemoveRemovalsObserver(WidgetRemovalsObserver* observer) { + removals_observers_.RemoveObserver(observer); +} + +bool Widget::HasRemovalsObserver(WidgetRemovalsObserver* observer) { + return removals_observers_.HasObserver(observer); +} + +bool Widget::GetAccelerator(int cmd_id, ui::Accelerator* accelerator) const { return false; } @@ -451,6 +446,12 @@ void Widget::NotifyNativeViewHierarchyChanged() { root_view_->NotifyNativeViewHierarchyChanged(); } +void Widget::NotifyWillRemoveView(View* view) { + FOR_EACH_OBSERVER(WidgetRemovalsObserver, + removals_observers_, + OnWillRemoveView(this, view)); +} + // Converted methods (see header) ---------------------------------------------- Widget* Widget::GetTopLevelWidget() { @@ -616,7 +617,9 @@ void Widget::Show() { // it is subsequently shown after being hidden. saved_show_state_ = ui::SHOW_STATE_NORMAL; } else { - native_widget_->Show(); + CanActivate() + ? native_widget_->Show() + : native_widget_->ShowWithWindowState(ui::SHOW_STATE_INACTIVE); } } @@ -661,6 +664,10 @@ bool Widget::IsAlwaysOnTop() const { return native_widget_->IsAlwaysOnTop(); } +void Widget::SetVisibleOnAllWorkspaces(bool always_visible) { + native_widget_->SetVisibleOnAllWorkspaces(always_visible); +} + void Widget::Maximize() { native_widget_->Maximize(); } @@ -770,6 +777,10 @@ const InputMethod* Widget::GetInputMethod() const { } } +ui::InputMethod* Widget::GetHostInputMethod() { + return native_widget_private()->GetHostInputMethod(); +} + void Widget::RunShellDrag(View* view, const ui::OSExchangeData& data, const gfx::Point& location, @@ -811,7 +822,7 @@ void Widget::UpdateWindowTitle() { // 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::string16 window_title = widget_delegate_->GetWindowTitle(); base::i18n::AdjustStringForLocaleDirection(&window_title); if (!native_widget_->SetWindowTitle(window_title)) return; @@ -876,6 +887,10 @@ bool Widget::ShouldUseNativeFrame() const { return native_widget_->ShouldUseNativeFrame(); } +bool Widget::ShouldWindowContentsBeTransparent() const { + return native_widget_->ShouldWindowContentsBeTransparent(); +} + void Widget::DebugToggleFrameType() { if (frame_type_ == FRAME_TYPE_DEFAULT) { frame_type_ = ShouldUseNativeFrame() ? FRAME_TYPE_FORCE_CUSTOM : @@ -949,20 +964,6 @@ const TooltipManager* Widget::GetTooltipManager() const { return native_widget_->GetTooltipManager(); } -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; -} - gfx::Rect Widget::GetWorkAreaBoundsInScreen() const { return native_widget_->GetWorkAreaBoundsInScreen(); } @@ -972,7 +973,7 @@ void Widget::SynthesizeMouseMoveEvent() { ui::MouseEvent mouse_event(ui::ET_MOUSE_MOVED, last_mouse_event_position_, last_mouse_event_position_, - ui::EF_IS_SYNTHESIZED); + ui::EF_IS_SYNTHESIZED, 0); root_view_->OnMouseMoved(mouse_event); } @@ -980,6 +981,10 @@ void Widget::OnRootViewLayout() { native_widget_->OnRootViewLayout(); } +bool Widget::IsTranslucentWindowOpacitySupported() const { + return native_widget_->IsTranslucentWindowOpacitySupported(); +} + void Widget::OnOwnerClosing() { } @@ -1015,6 +1020,22 @@ void Widget::OnNativeWidgetActivationChanged(bool active) { FOR_EACH_OBSERVER(WidgetObserver, observers_, OnWidgetActivationChanged(this, active)); + + // During window creation, the widget gets focused without activation, and in + // that case, the focus manager cannot set the appropriate text input client + // because the widget is not active. Thus we have to notify the focus manager + // not only when the focus changes but also when the widget gets activated. + // See crbug.com/377479 for details. + views::FocusManager* focus_manager = GetFocusManager(); + if (focus_manager) { + if (active) + focus_manager->FocusTextInputClient(focus_manager->GetFocusedView()); + else + focus_manager->BlurTextInputClient(focus_manager->GetFocusedView()); + } + + if (IsVisible() && non_client_view()) + non_client_view()->frame_view()->SchedulePaint(); } void Widget::OnNativeFocus(gfx::NativeView old_focused_view) { @@ -1069,24 +1090,37 @@ void Widget::OnNativeWidgetDestroyed() { native_widget_destroyed_ = true; } -gfx::Size Widget::GetMinimumSize() { +gfx::Size Widget::GetMinimumSize() const { return non_client_view_ ? non_client_view_->GetMinimumSize() : gfx::Size(); } -gfx::Size Widget::GetMaximumSize() { +gfx::Size Widget::GetMaximumSize() const { return non_client_view_ ? non_client_view_->GetMaximumSize() : gfx::Size(); } void Widget::OnNativeWidgetMove() { widget_delegate_->OnWidgetMove(); + View* root = GetRootView(); + if (root && root->GetFocusManager()) { + View* focused_view = root->GetFocusManager()->GetFocusedView(); + if (focused_view && focused_view->GetInputMethod()) + focused_view->GetInputMethod()->OnCaretBoundsChanged(focused_view); + } FOR_EACH_OBSERVER(WidgetObserver, observers_, OnWidgetBoundsChanged( this, GetWindowBoundsInScreen())); } void Widget::OnNativeWidgetSizeChanged(const gfx::Size& new_size) { - root_view_->SetSize(new_size); - + View* root = GetRootView(); + if (root) { + root->SetSize(new_size); + if (root->GetFocusManager()) { + View* focused_view = GetRootView()->GetFocusManager()->GetFocusedView(); + if (focused_view && focused_view->GetInputMethod()) + focused_view->GetInputMethod()->OnCaretBoundsChanged(focused_view); + } + } // Size changed notifications can fire prior to full initialization // i.e. during session restore. Avoid saving session state during these // startup procedures. @@ -1123,7 +1157,7 @@ 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); + GetRootView()->Paint(canvas, CullSet()); } int Widget::GetNonClientComponent(const gfx::Point& point) { @@ -1138,10 +1172,12 @@ int Widget::GetNonClientComponent(const gfx::Point& point) { } void Widget::OnKeyEvent(ui::KeyEvent* event) { - static_cast<internal::RootView*>(GetRootView())-> - DispatchKeyEvent(event); + SendEventToProcessor(event); } +// TODO(tdanderson): We should not be calling the OnMouse*() functions on +// RootView from anywhere in Widget. Use +// SendEventToProcessor() instead. See crbug.com/348087. void Widget::OnMouseEvent(ui::MouseEvent* event) { View* root_view = GetRootView(); switch (event->type()) { @@ -1172,6 +1208,7 @@ void Widget::OnMouseEvent(ui::MouseEvent* event) { } return; } + case ui::ET_MOUSE_RELEASED: last_mouse_event_was_move_ = false; is_mouse_button_pressed_ = false; @@ -1183,6 +1220,7 @@ void Widget::OnMouseEvent(ui::MouseEvent* 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_) { @@ -1197,20 +1235,22 @@ void Widget::OnMouseEvent(ui::MouseEvent* event) { 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() { @@ -1223,14 +1263,15 @@ void Widget::OnMouseCaptureLost() { 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); + ui::ScrollEvent event_copy(*event); + SendEventToProcessor(&event_copy); + + // Convert unhandled ui::ET_SCROLL events into ui::ET_MOUSEWHEEL events. + if (!event_copy.handled() && event_copy.type() == ui::ET_SCROLL) { + ui::MouseWheelEvent wheel(*event); + OnMouseEvent(&wheel); + } } void Widget::OnGestureEvent(ui::GestureEvent* event) { @@ -1243,17 +1284,14 @@ void Widget::OnGestureEvent(ui::GestureEvent* event) { break; case ui::ET_GESTURE_END: - if (event->details().touch_points() == 1) { + if (event->details().touch_points() == 1) is_touch_down_ = false; - if (auto_release_capture_) - ReleaseCapture(); - } break; default: break; } - static_cast<internal::RootView*>(GetRootView())->DispatchGestureEvent(event); + SendEventToProcessor(event); } bool Widget::ExecuteCommand(int command_id) { @@ -1290,6 +1328,27 @@ const Widget* Widget::AsWidget() const { return this; } +bool Widget::SetInitialFocus(ui::WindowShowState show_state) { + View* v = widget_delegate_->GetInitiallyFocusedView(); + if (!focus_on_creation_ || show_state == ui::SHOW_STATE_INACTIVE || + show_state == ui::SHOW_STATE_MINIMIZED) { + // 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; +} + +//////////////////////////////////////////////////////////////////////////////// +// Widget, ui::EventSource implementation: +ui::EventProcessor* Widget::GetEventProcessor() { + return root_view_.get(); +} + //////////////////////////////////////////////////////////////////////////////// // Widget, FocusTraversable implementation: @@ -1312,6 +1371,21 @@ View* Widget::GetFocusTraversableParentView() { } //////////////////////////////////////////////////////////////////////////////// +// Widget, ui::NativeThemeObserver implementation: + +void Widget::OnNativeThemeUpdated(ui::NativeTheme* observed_theme) { + DCHECK(observer_manager_.IsObserving(observed_theme)); + + ui::NativeTheme* current_native_theme = GetNativeTheme(); + if (!observer_manager_.IsObserving(current_native_theme)) { + observer_manager_.RemoveAll(); + observer_manager_.Add(current_native_theme); + } + + root_view_->PropagateNativeThemeChanged(current_native_theme); +} + +//////////////////////////////////////////////////////////////////////////////// // Widget, protected: internal::RootView* Widget::CreateRootView() { diff --git a/chromium/ui/views/widget/widget.h b/chromium/ui/views/widget/widget.h index ce14d7c5bff..c76633b282f 100644 --- a/chromium/ui/views/widget/widget.h +++ b/chromium/ui/views/widget/widget.h @@ -12,10 +12,13 @@ #include "base/gtest_prod_util.h" #include "base/memory/scoped_ptr.h" #include "base/observer_list.h" +#include "base/scoped_observer.h" +#include "ui/aura/window_layer_type.h" #include "ui/base/ui_base_types.h" -#include "ui/compositor/layer_type.h" +#include "ui/events/event_source.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/rect.h" +#include "ui/native_theme/native_theme_observer.h" #include "ui/views/focus/focus_manager.h" #include "ui/views/widget/native_widget_delegate.h" #include "ui/views/window/client_view.h" @@ -44,6 +47,7 @@ namespace ui { class Accelerator; class Compositor; class DefaultThemeProvider; +class InputMethod; class Layer; class NativeTheme; class OSExchangeData; @@ -52,7 +56,7 @@ class ThemeProvider; namespace views { -class DesktopRootWindowHost; +class DesktopWindowTreeHost; class InputMethod; class NativeWidget; class NonClientFrameView; @@ -60,6 +64,7 @@ class TooltipManager; class View; class WidgetDelegate; class WidgetObserver; +class WidgetRemovalsObserver; namespace internal { class NativeWidgetPrivate; @@ -92,7 +97,9 @@ class RootView; // destructor). // class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, - public FocusTraversable { + public ui::EventSource, + public FocusTraversable, + public ui::NativeThemeObserver { public: typedef std::set<Widget*> Widgets; @@ -155,6 +162,14 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, TRANSLUCENT_WINDOW, }; + enum Activatable { + // Infer whether the window should be activatable from the window type. + ACTIVATABLE_DEFAULT, + + ACTIVATABLE_YES, + ACTIVATABLE_NO + }; + enum Ownership { // Default. Creator is not responsible for managing the lifetime of the // Widget, it is destroyed when the corresponding NativeWidget is @@ -165,33 +180,42 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, WIDGET_OWNS_NATIVE_WIDGET }; + enum ShadowType { + SHADOW_TYPE_DEFAULT, // Use default shadow setting. It will be one of + // the settings below depending on InitParams::type + // and the native widget's type. + SHADOW_TYPE_NONE, // Don't draw any shadow. + SHADOW_TYPE_DROP, // Draw a drop shadow that emphasizes Z-order + // relationship to other windows. + }; + InitParams(); explicit InitParams(Type type); ~InitParams(); - // 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. + // Translucent windows may not always be supported. Use + // IsTranslucentWindowOpacitySupported to determine if translucent windows + // are supported. // 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; + Activatable activatable; bool keep_on_top; + bool visible_on_all_workspaces; Ownership ownership; bool mirror_origin_in_rtl; - bool has_dropshadow; - // Only used by Windows. Specifies that the system default caption and icon - // should not be rendered, and that the client area should be equivalent to - // the window area. + ShadowType shadow_type; + // Specifies that the system default caption and icon should not be + // rendered, and that the client area should be equivalent to the window + // area. Only used on some platforms (Windows and Linux). bool remove_standard_frame; // Only used by ShellWindow on Windows. Specifies that the default icon of // packaged app should be the system default icon. @@ -209,18 +233,14 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // 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 + // Aura-only. Provides a DesktopWindowTreeHost 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; + DesktopWindowTreeHost* desktop_window_tree_host; // Only used by NativeWidgetAura. Specifies the type of layer for the - // aura::Window. Default is LAYER_TEXTURED. - ui::LayerType layer_type; + // aura::Window. Default is WINDOW_LAYER_TEXTURED. + aura::WindowLayerType 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|; @@ -229,6 +249,9 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // where it wants your window placed.) NULL is not allowed if you are using // aura. gfx::NativeView context; + // If true, forces the window to be shown in the taskbar, even for window + // types that do not appear in the taskbar by default (popup and bubble). + bool force_show_in_taskbar; // Only used by X11, for root level windows. Specifies the res_name and // res_class fields, respectively, of the WM_CLASS window property. Controls // window grouping and desktop file matching in Linux window managers. @@ -253,9 +276,9 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // Creates a decorated window Widget with the specified properties. static Widget* CreateWindowWithParent(WidgetDelegate* delegate, - gfx::NativeWindow parent); + gfx::NativeView parent); static Widget* CreateWindowWithParentAndBounds(WidgetDelegate* delegate, - gfx::NativeWindow parent, + gfx::NativeView parent, const gfx::Rect& bounds); // Creates a decorated window Widget in the same desktop context as |context|. @@ -265,15 +288,6 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, gfx::NativeView context, const gfx::Rect& bounds); - // Creates an undecorated child window Widget parented to |parent|. - static Widget* CreateWindowAsFramelessChild(WidgetDelegate* widget_delegate, - gfx::NativeView parent); - - // 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(); @@ -341,9 +355,14 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, void RemoveObserver(WidgetObserver* observer); bool HasObserver(WidgetObserver* observer); + // Add/remove removals observer. + void AddRemovalsObserver(WidgetRemovalsObserver* observer); + void RemoveRemovalsObserver(WidgetRemovalsObserver* observer); + bool HasRemovalsObserver(WidgetRemovalsObserver* 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); + virtual bool GetAccelerator(int cmd_id, ui::Accelerator* accelerator) const; // Forwarded from the RootView so that the widget can do any cleanup. void ViewHierarchyChanged(const View::ViewHierarchyChangedDetails& details); @@ -356,6 +375,9 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // about the change. void NotifyNativeViewHierarchyChanged(); + // Called immediately before removing |view| from this widget. + void NotifyWillRemoveView(View* 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. @@ -434,8 +456,10 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // set to true after Close() has been invoked on the NativeWidget. bool IsClosed() const; - // Shows or hides the widget, without changing activation state. + // Shows the widget. The widget is activated if during initialization the + // can_activate flag in the InitParams structure is set to true. virtual void Show(); + // Hides the widget. void Hide(); // Like Show(), but does not activate the window. @@ -464,6 +488,9 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // in the windowing system. bool IsAlwaysOnTop() const; + // Sets the widget to be visible on all work spaces. + void SetVisibleOnAllWorkspaces(bool always_visible); + // Maximizes/minimizes/restores the window. void Maximize(); void Minimize(); @@ -527,6 +554,11 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, InputMethod* GetInputMethod(); const InputMethod* GetInputMethod() const; + // Returns the ui::InputMethod for this widget. + // TODO(yukishiino): Rename this method to GetInputMethod once we remove + // views::InputMethod. + ui::InputMethod* GetHostInputMethod(); + // 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 @@ -595,6 +627,10 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // Whether we should be using a native frame. bool ShouldUseNativeFrame() const; + // Determines whether the window contents should be rendered transparently + // (for example, so that they can overhang onto the window title bar). + bool ShouldWindowContentsBeTransparent() const; + // Forces the frame into the alternate frame type (custom or native) depending // on its current state. void DebugToggleFrameType(); @@ -668,12 +704,6 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, TooltipManager* GetTooltipManager(); const TooltipManager* GetTooltipManager() const; - // 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; } @@ -699,6 +729,9 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // window sizing information to the window server on some platforms. void OnRootViewLayout(); + // Whether the widget supports translucency. + bool IsTranslucentWindowOpacitySupported() const; + // 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 @@ -719,8 +752,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, 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 gfx::Size GetMinimumSize() const OVERRIDE; + virtual gfx::Size GetMaximumSize() const OVERRIDE; virtual void OnNativeWidgetMove() OVERRIDE; virtual void OnNativeWidgetSizeChanged(const gfx::Size& new_size) OVERRIDE; virtual void OnNativeWidgetBeginUserBoundsChange() OVERRIDE; @@ -733,7 +766,6 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, 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; @@ -743,12 +775,19 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE; virtual Widget* AsWidget() OVERRIDE; virtual const Widget* AsWidget() const OVERRIDE; + virtual bool SetInitialFocus(ui::WindowShowState show_state) OVERRIDE; + + // Overridden from ui::EventSource: + virtual ui::EventProcessor* GetEventProcessor() OVERRIDE; // Overridden from FocusTraversable: virtual FocusSearch* GetFocusSearch() OVERRIDE; virtual FocusTraversable* GetFocusTraversableParent() OVERRIDE; virtual View* GetFocusTraversableParentView() OVERRIDE; + // Overridden from ui::NativeThemeObserver: + virtual void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) OVERRIDE; + protected: // Creates the RootView to be used within this Widget. Subclasses may override // to create custom RootViews that do specialized event processing. @@ -762,7 +801,7 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, private: friend class ComboboxTest; - friend class NativeTextfieldViewsTest; + friend class TextfieldTest; // Sets the value of |disable_inactive_rendering_|. If the value changes, // both the NonClientView and WidgetDelegate are notified. @@ -795,6 +834,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, ObserverList<WidgetObserver> observers_; + ObserverList<WidgetRemovalsObserver> removals_observers_; + // Non-owned pointer to the Widget's delegate. If a NULL delegate is supplied // to Init() a default WidgetDelegate is created. WidgetDelegate* widget_delegate_; @@ -892,6 +933,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // disabled. bool movement_disabled_; + ScopedObserver<ui::NativeTheme, ui::NativeThemeObserver> observer_manager_; + DISALLOW_COPY_AND_ASSIGN(Widget); }; diff --git a/chromium/ui/views/widget/widget_aura_utils.cc b/chromium/ui/views/widget/widget_aura_utils.cc index 348f828ff8f..be284a8af81 100644 --- a/chromium/ui/views/widget/widget_aura_utils.cc +++ b/chromium/ui/views/widget/widget_aura_utils.cc @@ -8,27 +8,27 @@ namespace views { -aura::client::WindowType GetAuraWindowTypeForWidgetType( +ui::wm::WindowType GetAuraWindowTypeForWidgetType( Widget::InitParams::Type type) { switch (type) { case Widget::InitParams::TYPE_WINDOW: - return aura::client::WINDOW_TYPE_NORMAL; + return ui::wm::WINDOW_TYPE_NORMAL; case Widget::InitParams::TYPE_PANEL: - return aura::client::WINDOW_TYPE_PANEL; + return ui::wm::WINDOW_TYPE_PANEL; case Widget::InitParams::TYPE_CONTROL: - return aura::client::WINDOW_TYPE_CONTROL; + return ui::wm::WINDOW_TYPE_CONTROL; case Widget::InitParams::TYPE_WINDOW_FRAMELESS: case Widget::InitParams::TYPE_POPUP: case Widget::InitParams::TYPE_BUBBLE: case Widget::InitParams::TYPE_DRAG: - return aura::client::WINDOW_TYPE_POPUP; + return ui::wm::WINDOW_TYPE_POPUP; case Widget::InitParams::TYPE_MENU: - return aura::client::WINDOW_TYPE_MENU; + return ui::wm::WINDOW_TYPE_MENU; case Widget::InitParams::TYPE_TOOLTIP: - return aura::client::WINDOW_TYPE_TOOLTIP; + return ui::wm::WINDOW_TYPE_TOOLTIP; default: NOTREACHED() << "Unhandled widget type " << type; - return aura::client::WINDOW_TYPE_UNKNOWN; + return ui::wm::WINDOW_TYPE_UNKNOWN; } } diff --git a/chromium/ui/views/widget/widget_aura_utils.h b/chromium/ui/views/widget/widget_aura_utils.h index 8f0bfcc0358..4b16de48cab 100644 --- a/chromium/ui/views/widget/widget_aura_utils.h +++ b/chromium/ui/views/widget/widget_aura_utils.h @@ -5,14 +5,14 @@ #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" +#include "ui/wm/public/window_types.h" // Functions shared by native_widget_aura.cc and desktop_native_widget_aura.cc: namespace views { -aura::client::WindowType GetAuraWindowTypeForWidgetType( +ui::wm::WindowType GetAuraWindowTypeForWidgetType( Widget::InitParams::Type type); } // namespace views diff --git a/chromium/ui/views/widget/widget_delegate.cc b/chromium/ui/views/widget/widget_delegate.cc index fbace27c316..7e1f67e19c3 100644 --- a/chromium/ui/views/widget/widget_delegate.cc +++ b/chromium/ui/views/widget/widget_delegate.cc @@ -17,7 +17,9 @@ namespace views { //////////////////////////////////////////////////////////////////////////////// // WidgetDelegate: -WidgetDelegate::WidgetDelegate() : default_contents_view_(NULL) { +WidgetDelegate::WidgetDelegate() + : default_contents_view_(NULL), + can_activate_(true) { } void WidgetDelegate::OnWidgetMove() { @@ -50,23 +52,23 @@ bool WidgetDelegate::CanMaximize() const { } bool WidgetDelegate::CanActivate() const { - return true; + return can_activate_; } ui::ModalType WidgetDelegate::GetModalType() const { return ui::MODAL_TYPE_NONE; } -ui::AccessibilityTypes::Role WidgetDelegate::GetAccessibleWindowRole() const { - return ui::AccessibilityTypes::ROLE_WINDOW; +ui::AXRole WidgetDelegate::GetAccessibleWindowRole() const { + return ui::AX_ROLE_WINDOW; } -string16 WidgetDelegate::GetAccessibleWindowTitle() const { +base::string16 WidgetDelegate::GetAccessibleWindowTitle() const { return GetWindowTitle(); } -string16 WidgetDelegate::GetWindowTitle() const { - return string16(); +base::string16 WidgetDelegate::GetWindowTitle() const { + return base::string16(); } bool WidgetDelegate::ShouldShowWindowTitle() const { diff --git a/chromium/ui/views/widget/widget_delegate.h b/chromium/ui/views/widget/widget_delegate.h index 16e29a8b338..b9903096c14 100644 --- a/chromium/ui/views/widget/widget_delegate.h +++ b/chromium/ui/views/widget/widget_delegate.h @@ -8,7 +8,7 @@ #include <string> #include <vector> -#include "ui/base/accessibility/accessibility_types.h" +#include "ui/accessibility/ax_enums.h" #include "ui/base/ui_base_types.h" #include "ui/views/view.h" @@ -30,6 +30,11 @@ class VIEWS_EXPORT WidgetDelegate { public: WidgetDelegate(); + // Sets the return value of CanActivate(). Default is true. + void set_can_activate(bool can_activate) { + can_activate_ = can_activate; + } + // Called whenever the widget's position changes. virtual void OnWidgetMove(); @@ -60,13 +65,13 @@ class VIEWS_EXPORT WidgetDelegate { // ui::MODAL_TYPE_NONE (not modal). virtual ui::ModalType GetModalType() const; - virtual ui::AccessibilityTypes::Role GetAccessibleWindowRole() const; + virtual ui::AXRole GetAccessibleWindowRole() const; // Returns the title to be read with screen readers. - virtual string16 GetAccessibleWindowTitle() const; + virtual base::string16 GetAccessibleWindowTitle() const; // Returns the text to be displayed in the window title. - virtual string16 GetWindowTitle() const; + virtual base::string16 GetWindowTitle() const; // Returns true if the window should show a title in the title bar. virtual bool ShouldShowWindowTitle() const; @@ -181,6 +186,8 @@ class VIEWS_EXPORT WidgetDelegate { private: View* default_contents_view_; + bool can_activate_; + DISALLOW_COPY_AND_ASSIGN(WidgetDelegate); }; diff --git a/chromium/ui/views/widget/widget_hwnd_utils.cc b/chromium/ui/views/widget/widget_hwnd_utils.cc index 53588c25e5e..ac313bb74c8 100644 --- a/chromium/ui/views/widget/widget_hwnd_utils.cc +++ b/chromium/ui/views/widget/widget_hwnd_utils.cc @@ -41,7 +41,8 @@ void CalculateWindowStylesFromInitParams( *style |= WS_MINIMIZE; if (!params.accept_events) *ex_style |= WS_EX_TRANSPARENT; - if (!params.can_activate) + DCHECK_NE(Widget::InitParams::ACTIVATABLE_DEFAULT, params.activatable); + if (params.activatable == Widget::InitParams::ACTIVATABLE_NO) *ex_style |= WS_EX_NOACTIVATE; if (params.keep_on_top) *ex_style |= WS_EX_TOPMOST; @@ -62,14 +63,10 @@ void CalculateWindowStylesFromInitParams( // 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) { + if (params.shadow_type == Widget::InitParams::SHADOW_TYPE_DROP) { *class_style |= (base::win::GetVersion() < base::win::VERSION_XP) ? 0 : CS_DROPSHADOW; } @@ -122,10 +119,13 @@ void CalculateWindowStylesFromInitParams( case Widget::InitParams::TYPE_BUBBLE: *style |= WS_POPUP; *style |= WS_CLIPCHILDREN; + if (!params.force_show_in_taskbar) + *ex_style |= WS_EX_TOOLWINDOW; break; case Widget::InitParams::TYPE_POPUP: *style |= WS_POPUP; - *ex_style |= WS_EX_TOOLWINDOW; + if (!params.force_show_in_taskbar) + *ex_style |= WS_EX_TOOLWINDOW; break; case Widget::InitParams::TYPE_MENU: *style |= WS_POPUP; diff --git a/chromium/ui/views/widget/widget_hwnd_utils.h b/chromium/ui/views/widget/widget_hwnd_utils.h index 2808bab0d48..09ee0abe138 100644 --- a/chromium/ui/views/widget/widget_hwnd_utils.h +++ b/chromium/ui/views/widget/widget_hwnd_utils.h @@ -9,7 +9,8 @@ #include "ui/views/widget/widget.h" -// Functions shared by native_widget_win.cc and desktop_root_window_host_win.cc: +// Functions shared by hwnd_message_handler.cc and +// desktop_window_tree_host_win.cc: namespace views { class HWNDMessageHandler; diff --git a/chromium/ui/views/widget/widget_interactive_uitest.cc b/chromium/ui/views/widget/widget_interactive_uitest.cc index 30d44a3c9d7..45026a9eb1e 100644 --- a/chromium/ui/views/widget/widget_interactive_uitest.cc +++ b/chromium/ui/views/widget/widget_interactive_uitest.cc @@ -4,21 +4,31 @@ #include "base/basictypes.h" #include "base/bind.h" +#include "base/command_line.h" +#include "base/path_service.h" #include "base/run_loop.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "ui/aura/client/focus_client.h" +#include "ui/aura/env.h" +#include "ui/aura/test/event_generator.h" +#include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/base/ui_base_paths.h" +#include "ui/base/ui_base_switches.h" +#include "ui/events/event_processor.h" #include "ui/gfx/native_widget_types.h" +#include "ui/gl/gl_surface.h" +#include "ui/views/controls/textfield/textfield.h" +#include "ui/views/controls/textfield/textfield_test_api.h" #include "ui/views/test/widget_test.h" +#include "ui/views/touchui/touch_selection_controller_impl.h" #include "ui/views/widget/widget.h" #include "ui/views/window/dialog_delegate.h" +#include "ui/wm/public/activation_client.h" -#if defined(USE_AURA) -#include "ui/aura/client/activation_client.h" -#include "ui/aura/client/focus_client.h" -#include "ui/aura/env.h" -#include "ui/aura/root_window.h" -#include "ui/aura/window.h" -#endif - -#if defined(USE_AURA) && !defined(OS_CHROMEOS) +#if !defined(OS_CHROMEOS) #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" #endif @@ -134,9 +144,6 @@ class NestedLoopCaptureView : public View { 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; } @@ -148,8 +155,39 @@ class NestedLoopCaptureView : public View { } // namespace -#if defined(OS_WIN) && defined(USE_AURA) -// Tests whether activation and focus change works correctly in Windows AURA. +class WidgetTestInteractive : public WidgetTest { + public: + WidgetTestInteractive() {} + virtual ~WidgetTestInteractive() {} + + virtual void SetUp() OVERRIDE { + gfx::GLSurface::InitializeOneOffForTests(); + base::FilePath pak_dir; + PathService::Get(base::DIR_MODULE, &pak_dir); + base::FilePath pak_file; + pak_file = pak_dir.Append(FILE_PATH_LITERAL("ui_test.pak")); + ui::ResourceBundle::InitSharedInstanceWithPakPath(pak_file); + WidgetTest::SetUp(); + } + + protected: + static void ShowQuickMenuImmediately( + TouchSelectionControllerImpl* controller) { + DCHECK(controller); + if (controller->context_menu_timer_.IsRunning()) { + controller->context_menu_timer_.Stop(); + controller->ContextMenuTimerFired(); + } + } + + static bool IsQuickMenuVisible(TouchSelectionControllerImpl* controller) { + DCHECK(controller); + return controller->context_menu_ && controller->context_menu_->visible(); + } +}; + +#if defined(OS_WIN) +// Tests whether activation and focus change works correctly in Windows. // We test the following:- // 1. If the active aura window is correctly set when a top level widget is // created. @@ -157,9 +195,8 @@ class NestedLoopCaptureView : public View { // another top level widget is created and focused. // 3. On focusing the native platform window for widget 1, the active aura // window for widget 1 should be set and that for widget 2 should reset. -// TODO(ananta) -// Discuss with erg on how to write this test for linux x11 aura. -TEST_F(WidgetTest, DesktopNativeWidgetAuraActivationAndFocusTest) { +// TODO(ananta): Discuss with erg on how to write this test for linux x11 aura. +TEST_F(WidgetTestInteractive, DesktopNativeWidgetAuraActivationAndFocusTest) { // Create widget 1 and expect the active window to be its window. View* contents_view1 = new View; contents_view1->SetFocusable(true); @@ -195,7 +232,7 @@ TEST_F(WidgetTest, DesktopNativeWidgetAuraActivationAndFocusTest) { aura::Window* root_window2 = widget2.GetNativeView()->GetRootWindow(); contents_view2->RequestFocus(); ::SetActiveWindow( - root_window2->GetDispatcher()->host()->GetAcceleratedWidget()); + root_window2->GetHost()->GetAcceleratedWidget()); aura::client::ActivationClient* activation_client2 = aura::client::GetActivationClient(root_window2); @@ -208,14 +245,14 @@ TEST_F(WidgetTest, DesktopNativeWidgetAuraActivationAndFocusTest) { // window. contents_view1->RequestFocus(); ::SetActiveWindow( - root_window1->GetDispatcher()->host()->GetAcceleratedWidget()); + root_window1->GetHost()->GetAcceleratedWidget()); EXPECT_EQ(activation_client2->GetActiveWindow(), reinterpret_cast<aura::Window*>(NULL)); EXPECT_EQ(activation_client1->GetActiveWindow(), widget1.GetNativeView()); } #endif -TEST_F(WidgetTest, CaptureAutoReset) { +TEST_F(WidgetTestInteractive, CaptureAutoReset) { Widget* toplevel = CreateTopLevelFramelessPlatformWidget(); View* container = new View; toplevel->SetContentsView(container); @@ -227,7 +264,7 @@ TEST_F(WidgetTest, CaptureAutoReset) { // By default, mouse release removes capture. gfx::Point click_location(45, 15); ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location, - ui::EF_LEFT_MOUSE_BUTTON); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); toplevel->OnMouseEvent(&release); EXPECT_FALSE(toplevel->HasCapture()); @@ -244,7 +281,7 @@ TEST_F(WidgetTest, CaptureAutoReset) { RunPendingMessages(); } -TEST_F(WidgetTest, ResetCaptureOnGestureEnd) { +TEST_F(WidgetTestInteractive, ResetCaptureOnGestureEnd) { Widget* toplevel = CreateTopLevelFramelessPlatformWidget(); View* container = new View; toplevel->SetContentsView(container); @@ -274,9 +311,9 @@ TEST_F(WidgetTest, ResetCaptureOnGestureEnd) { gfx::Point click_location(45, 15); ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location, - ui::EF_LEFT_MOUSE_BUTTON); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location, - ui::EF_LEFT_MOUSE_BUTTON); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); EXPECT_TRUE(toplevel->HasCapture()); @@ -300,7 +337,7 @@ TEST_F(WidgetTest, ResetCaptureOnGestureEnd) { // 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, DisableCaptureWidgetFromMousePress) { +TEST_F(WidgetTestInteractive, DisableCaptureWidgetFromMousePress) { // 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 @@ -327,9 +364,10 @@ TEST_F(WidgetTest, DisableCaptureWidgetFromMousePress) { base::Owned(new ui::MouseEvent(ui::ET_MOUSE_RELEASED, location, location, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)))); ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location, - ui::EF_LEFT_MOUSE_BUTTON); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); first->OnMouseEvent(&press); EXPECT_FALSE(first->HasCapture()); first->Close(); @@ -338,7 +376,7 @@ TEST_F(WidgetTest, DisableCaptureWidgetFromMousePress) { // Tests some grab/ungrab events. // TODO(estade): can this be enabled now that this is an interactive ui test? -TEST_F(WidgetTest, DISABLED_GrabUngrab) { +TEST_F(WidgetTestInteractive, DISABLED_GrabUngrab) { Widget* toplevel = CreateTopLevelPlatformWidget(); Widget* child1 = CreateChildNativeWidgetWithParent(toplevel); Widget* child2 = CreateChildNativeWidgetWithParent(toplevel); @@ -361,7 +399,7 @@ TEST_F(WidgetTest, DISABLED_GrabUngrab) { // Click on child1 gfx::Point p1(45, 45); ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1, - ui::EF_LEFT_MOUSE_BUTTON); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); toplevel->OnMouseEvent(&pressed); EXPECT_TRUE(toplevel->HasCapture()); @@ -369,7 +407,7 @@ TEST_F(WidgetTest, DISABLED_GrabUngrab) { EXPECT_FALSE(child2->HasCapture()); ui::MouseEvent released(ui::ET_MOUSE_RELEASED, p1, p1, - ui::EF_LEFT_MOUSE_BUTTON); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); toplevel->OnMouseEvent(&released); EXPECT_FALSE(toplevel->HasCapture()); @@ -381,7 +419,7 @@ TEST_F(WidgetTest, DISABLED_GrabUngrab) { // Click on child2 gfx::Point p2(315, 45); ui::MouseEvent pressed2(ui::ET_MOUSE_PRESSED, p2, p2, - ui::EF_LEFT_MOUSE_BUTTON); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); toplevel->OnMouseEvent(&pressed2); EXPECT_TRUE(pressed2.handled()); EXPECT_TRUE(toplevel->HasCapture()); @@ -389,7 +427,7 @@ TEST_F(WidgetTest, DISABLED_GrabUngrab) { EXPECT_FALSE(child1->HasCapture()); ui::MouseEvent released2(ui::ET_MOUSE_RELEASED, p2, p2, - ui::EF_LEFT_MOUSE_BUTTON); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); toplevel->OnMouseEvent(&released2); EXPECT_FALSE(toplevel->HasCapture()); EXPECT_FALSE(child1->HasCapture()); @@ -400,7 +438,7 @@ TEST_F(WidgetTest, DISABLED_GrabUngrab) { // 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) { +TEST_F(WidgetTestInteractive, CheckResizeControllerEvents) { Widget* toplevel = CreateTopLevelPlatformWidget(); toplevel->SetBounds(gfx::Rect(0, 0, 100, 100)); @@ -414,21 +452,24 @@ TEST_F(WidgetTest, CheckResizeControllerEvents) { // Move to an outside position. gfx::Point p1(200, 200); - ui::MouseEvent moved_out(ui::ET_MOUSE_MOVED, p1, p1, ui::EF_NONE); + ui::MouseEvent moved_out(ui::ET_MOUSE_MOVED, p1, p1, ui::EF_NONE, + 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); + ui::MouseEvent moved_over(ui::ET_MOUSE_MOVED, p2, p2, ui::EF_NONE, + 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); + ui::MouseEvent moved_resizer(ui::ET_MOUSE_MOVED, p3, p3, ui::EF_NONE, + ui::EF_NONE); toplevel->OnMouseEvent(&moved_resizer); EXPECT_EQ(0, view->EnteredCalls()); EXPECT_EQ(1, view->ExitedCalls()); @@ -443,8 +484,92 @@ TEST_F(WidgetTest, CheckResizeControllerEvents) { toplevel->CloseNow(); } +// Test view focus restoration when a widget is deactivated and re-activated. +TEST_F(WidgetTestInteractive, ViewFocusOnWidgetActivationChanges) { + Widget* widget1 = CreateTopLevelPlatformWidget(); + View* view1 = new View; + view1->SetFocusable(true); + widget1->GetContentsView()->AddChildView(view1); + + Widget* widget2 = CreateTopLevelPlatformWidget(); + View* view2a = new View; + View* view2b = new View; + view2a->SetFocusable(true); + view2b->SetFocusable(true); + widget2->GetContentsView()->AddChildView(view2a); + widget2->GetContentsView()->AddChildView(view2b); + + widget1->Show(); + EXPECT_TRUE(widget1->IsActive()); + view1->RequestFocus(); + EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView()); + + widget2->Show(); + EXPECT_TRUE(widget2->IsActive()); + EXPECT_FALSE(widget1->IsActive()); + EXPECT_EQ(NULL, widget1->GetFocusManager()->GetFocusedView()); + view2a->RequestFocus(); + EXPECT_EQ(view2a, widget2->GetFocusManager()->GetFocusedView()); + view2b->RequestFocus(); + EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView()); + + widget1->Activate(); + EXPECT_TRUE(widget1->IsActive()); + EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView()); + EXPECT_FALSE(widget2->IsActive()); + EXPECT_EQ(NULL, widget2->GetFocusManager()->GetFocusedView()); + + widget2->Activate(); + EXPECT_TRUE(widget2->IsActive()); + EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView()); + EXPECT_FALSE(widget1->IsActive()); + EXPECT_EQ(NULL, widget1->GetFocusManager()->GetFocusedView()); + + widget1->CloseNow(); + widget2->CloseNow(); +} + #if defined(OS_WIN) +// Test view focus retention when a widget's HWND is disabled and re-enabled. +TEST_F(WidgetTestInteractive, ViewFocusOnHWNDEnabledChanges) { + Widget* widget = CreateTopLevelFramelessPlatformWidget(); + widget->SetContentsView(new View); + for (size_t i = 0; i < 2; ++i) { + widget->GetContentsView()->AddChildView(new View); + widget->GetContentsView()->child_at(i)->SetFocusable(true); + } + + widget->Show(); + const HWND hwnd = HWNDForWidget(widget); + EXPECT_TRUE(::IsWindow(hwnd)); + EXPECT_TRUE(::IsWindowEnabled(hwnd)); + EXPECT_EQ(hwnd, ::GetActiveWindow()); + + for (int i = 0; i < widget->GetContentsView()->child_count(); ++i) { + SCOPED_TRACE(base::StringPrintf("Child view %d", i)); + View* view = widget->GetContentsView()->child_at(i); + + view->RequestFocus(); + EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView()); + EXPECT_FALSE(::EnableWindow(hwnd, FALSE)); + EXPECT_FALSE(::IsWindowEnabled(hwnd)); + + // Oddly, disabling the HWND leaves it active with the focus unchanged. + EXPECT_EQ(hwnd, ::GetActiveWindow()); + EXPECT_TRUE(widget->IsActive()); + EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView()); + + EXPECT_TRUE(::EnableWindow(hwnd, TRUE)); + EXPECT_TRUE(::IsWindowEnabled(hwnd)); + EXPECT_EQ(hwnd, ::GetActiveWindow()); + EXPECT_TRUE(widget->IsActive()); + EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView()); + } + + widget->CloseNow(); +} + // This class subclasses the Widget class to listen for activation change // notifications and provides accessors to return information as to whether // the widget is active. We need this to ensure that users of the widget @@ -473,23 +598,19 @@ class WidgetActivationTest : public Widget { // Tests whether the widget only becomes active when the underlying window // is really active. -TEST_F(WidgetTest, WidgetNotActivatedOnFakeActivationMessages) { +TEST_F(WidgetTestInteractive, WidgetNotActivatedOnFakeActivationMessages) { WidgetActivationTest widget1; Widget::InitParams init_params = CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; -#if defined(USE_AURA) init_params.native_widget = new DesktopNativeWidgetAura(&widget1); -#endif init_params.bounds = gfx::Rect(0, 0, 200, 200); widget1.Init(init_params); widget1.Show(); EXPECT_EQ(true, widget1.active()); WidgetActivationTest widget2; -#if defined(USE_AURA) init_params.native_widget = new DesktopNativeWidgetAura(&widget2); -#endif widget2.Init(init_params); widget2.Show(); EXPECT_EQ(true, widget2.active()); @@ -508,7 +629,7 @@ TEST_F(WidgetTest, WidgetNotActivatedOnFakeActivationMessages) { } #endif -#if defined(USE_AURA) && !defined(OS_CHROMEOS) +#if !defined(OS_CHROMEOS) // Provides functionality to create a window modal dialog. class ModalDialogDelegate : public DialogDelegateView { public: @@ -527,9 +648,8 @@ class ModalDialogDelegate : public DialogDelegateView { }; // Tests whether the focused window is set correctly when a modal window is -// created and destroyed. When it is destroyed it should focus the owner -// window. -TEST_F(WidgetTest, WindowModalWindowDestroyedActivationTest) { +// created and destroyed. When it is destroyed it should focus the owner window. +TEST_F(WidgetTestInteractive, WindowModalWindowDestroyedActivationTest) { // Create a top level widget. Widget top_level_widget; Widget::InitParams init_params = @@ -566,7 +686,7 @@ TEST_F(WidgetTest, WindowModalWindowDestroyedActivationTest) { } // Test that when opening a system-modal window, capture is released. -TEST_F(WidgetTest, SystemModalWindowReleasesCapture) { +TEST_F(WidgetTestInteractive, SystemModalWindowReleasesCapture) { // Create a top level widget. Widget top_level_widget; Widget::InitParams init_params = @@ -604,6 +724,59 @@ TEST_F(WidgetTest, SystemModalWindowReleasesCapture) { #endif +TEST_F(WidgetTestInteractive, CanActivateFlagIsHonored) { + Widget widget; + Widget::InitParams init_params = + CreateParams(Widget::InitParams::TYPE_WINDOW); + init_params.bounds = gfx::Rect(0, 0, 200, 200); + init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + init_params.activatable = Widget::InitParams::ACTIVATABLE_NO; +#if !defined(OS_CHROMEOS) + init_params.native_widget = new DesktopNativeWidgetAura(&widget); +#endif // !defined(OS_CHROMEOS) + widget.Init(init_params); + + widget.Show(); + EXPECT_FALSE(widget.IsActive()); +} + +// Test that touch selection quick menu is not activated when opened. +TEST_F(WidgetTestInteractive, TouchSelectionQuickMenuIsNotActivated) { + CommandLine::ForCurrentProcess()->AppendSwitch(switches::kEnableTouchEditing); +#if defined(OS_WIN) + views_delegate().set_use_desktop_native_widgets(true); +#endif // !defined(OS_WIN) + + 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; + widget.Init(init_params); + + Textfield* textfield = new Textfield; + textfield->SetBounds(0, 0, 200, 20); + textfield->SetText(base::ASCIIToUTF16("some text")); + widget.GetRootView()->AddChildView(textfield); + + widget.Show(); + textfield->RequestFocus(); + textfield->SelectAll(true); + TextfieldTestApi textfield_test_api(textfield); + + RunPendingMessages(); + + aura::test::EventGenerator generator(widget.GetNativeView()->GetRootWindow()); + generator.GestureTapAt(gfx::Point(10, 10)); + ShowQuickMenuImmediately(static_cast<TouchSelectionControllerImpl*>( + textfield_test_api.touch_selection_controller())); + + EXPECT_TRUE(textfield->HasFocus()); + EXPECT_TRUE(widget.IsActive()); + EXPECT_TRUE(IsQuickMenuVisible(static_cast<TouchSelectionControllerImpl*>( + textfield_test_api.touch_selection_controller()))); +} + namespace { // Used to veirfy OnMouseCaptureLost() has been invoked. @@ -640,6 +813,16 @@ class WidgetCaptureTest : public ViewsTestBase { virtual ~WidgetCaptureTest() { } + virtual void SetUp() OVERRIDE { + gfx::GLSurface::InitializeOneOffForTests(); + base::FilePath pak_dir; + PathService::Get(base::DIR_MODULE, &pak_dir); + base::FilePath pak_file; + pak_file = pak_dir.Append(FILE_PATH_LITERAL("ui_test.pak")); + ui::ResourceBundle::InitSharedInstanceWithPakPath(pak_file); + ViewsTestBase::SetUp(); + } + // Verifies Widget::SetCapture() results in updating native capture along with // invoking the right Widget function. void TestCapture(bool use_desktop_native_widget) { @@ -686,7 +869,7 @@ class WidgetCaptureTest : public ViewsTestBase { private: NativeWidget* CreateNativeWidget(bool create_desktop_native_widget, Widget* widget) { -#if defined(USE_AURA) && !defined(OS_CHROMEOS) +#if !defined(OS_CHROMEOS) if (create_desktop_native_widget) return new DesktopNativeWidgetAura(widget); #endif @@ -701,14 +884,14 @@ TEST_F(WidgetCaptureTest, Capture) { TestCapture(false); } -#if defined(USE_AURA) && !defined(OS_LINUX) +#if !defined(OS_LINUX) // See description in TestCapture(). Creates DesktopNativeWidget. TEST_F(WidgetCaptureTest, CaptureDesktopNativeWidget) { TestCapture(true); } #endif -#if defined(USE_AURA) && !defined(OS_CHROMEOS) +#if !defined(OS_CHROMEOS) namespace { // Used to veirfy OnMouseEvent() has been invoked. @@ -737,7 +920,7 @@ class MouseEventTrackingWidget : public Widget { } // namespace -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA) +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) // TODO(erg): linux_aura bringup: http://crbug.com/163931 #define MAYBE_MouseEventDispatchedToRightWindow \ DISABLED_MouseEventDispatchedToRightWindow @@ -775,9 +958,10 @@ TEST_F(WidgetCaptureTest, MAYBE_MouseEventDispatchedToRightWindow) { // Send a mouse event to the RootWindow associated with |widget1|. Even though // |widget2| has capture, |widget1| should still get the event. ui::MouseEvent mouse_event(ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(), - ui::EF_NONE); - widget1.GetNativeWindow()->GetDispatcher()->AsRootWindowHostDelegate()-> - OnHostMouseEvent(&mouse_event); + ui::EF_NONE, ui::EF_NONE); + ui::EventDispatchDetails details = widget1.GetNativeWindow()-> + GetHost()->event_processor()->OnEventFromSource(&mouse_event); + ASSERT_FALSE(details.dispatcher_destroyed); EXPECT_TRUE(widget1.GetAndClearGotMouseEvent()); EXPECT_FALSE(widget2.GetAndClearGotMouseEvent()); } diff --git a/chromium/ui/views/widget/widget_observer.h b/chromium/ui/views/widget/widget_observer.h index fc13afae682..2cad7142e25 100644 --- a/chromium/ui/views/widget/widget_observer.h +++ b/chromium/ui/views/widget/widget_observer.h @@ -14,6 +14,7 @@ class Rect; namespace views { class Widget; +class View; // Observers can listen to various events on the Widgets. class VIEWS_EXPORT WidgetObserver { diff --git a/chromium/ui/views/widget/widget_removals_observer.h b/chromium/ui/views/widget/widget_removals_observer.h new file mode 100644 index 00000000000..e62acab36a4 --- /dev/null +++ b/chromium/ui/views/widget/widget_removals_observer.h @@ -0,0 +1,30 @@ +// Copyright 2014 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_REMOVALS_OBSERVER_H_ +#define UI_VIEWS_WIDGET_WIDGET_REMOVALS_OBSERVER_H_ + +#include "ui/views/views_export.h" + +namespace views { + +class Widget; +class View; + +// |WidgetRemovalsObserver| complements |WidgetObserver| with additional +// notifications. These include events occurring during tear down like view +// removal. For this reason, it is recommended that subclasses not also inherit +// from |View|. +class VIEWS_EXPORT WidgetRemovalsObserver { + public: + // Called immediately before a descendant view of |widget| is removed. + virtual void OnWillRemoveView(Widget* widget, View* view) {} + + protected: + virtual ~WidgetRemovalsObserver() {} +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_WIDGET_REMOVALS_OBSERVER_H_ diff --git a/chromium/ui/views/widget/widget_unittest.cc b/chromium/ui/views/widget/widget_unittest.cc index a243eb5f2ac..d6062067a33 100644 --- a/chromium/ui/views/widget/widget_unittest.cc +++ b/chromium/ui/views/widget/widget_unittest.cc @@ -12,7 +12,12 @@ #include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/aura/test/event_generator.h" +#include "ui/aura/window.h" #include "ui/base/hit_test.h" +#include "ui/compositor/scoped_animation_duration_scale_mode.h" +#include "ui/compositor/scoped_layer_animation_settings.h" +#include "ui/events/event_processor.h" #include "ui/events/event_utils.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/point.h" @@ -23,23 +28,10 @@ #include "ui/views/views_delegate.h" #include "ui/views/widget/native_widget_delegate.h" #include "ui/views/widget/root_view.h" +#include "ui/views/widget/widget_deletion_observer.h" #include "ui/views/window/dialog_delegate.h" #include "ui/views/window/native_frame_view.h" -#if defined(USE_AURA) -#include "ui/aura/client/aura_constants.h" -#include "ui/aura/client/window_tree_client.h" -#include "ui/aura/root_window.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 - #if defined(OS_WIN) #include "ui/views/win/hwnd_util.h" #endif @@ -72,9 +64,6 @@ class EventCountView : public View { 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); } @@ -90,7 +79,7 @@ class EventCountView : public View { }; // A view that keeps track of the events it receives, and consumes all scroll -// gesture events. +// gesture events and ui::ET_SCROLL events. class ScrollableEventCountView : public EventCountView { public: ScrollableEventCountView() {} @@ -112,6 +101,12 @@ class ScrollableEventCountView : public EventCountView { } } + virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE { + EventCountView::OnScrollEvent(event); + if (event->type() == ui::ET_SCROLL) + event->SetHandled(); + } + DISALLOW_COPY_AND_ASSIGN(ScrollableEventCountView); }; @@ -123,7 +118,7 @@ class MinimumSizeFrameView : public NativeFrameView { private: // Overridden from View: - virtual gfx::Size GetMinimumSize() OVERRIDE { + virtual gfx::Size GetMinimumSize() const OVERRIDE { return gfx::Size(300, 400); } @@ -162,34 +157,47 @@ class EventCountHandler : public ui::EventHandler { DISALLOW_COPY_AND_ASSIGN(EventCountHandler); }; +// Class that closes the widget (which ends up deleting it immediately) when the +// appropriate event is received. +class CloseWidgetView : public View { + public: + explicit CloseWidgetView(ui::EventType event_type) + : event_type_(event_type) { + } + + // ui::EventHandler override: + virtual void OnEvent(ui::Event* event) OVERRIDE { + if (event->type() == event_type_) { + // Go through NativeWidgetPrivate to simulate what happens if the OS + // deletes the NativeWindow out from under us. + GetWidget()->native_widget_private()->CloseNow(); + } else { + View::OnEvent(event); + if (!event->IsTouchEvent()) + event->SetHandled(); + } + } + + private: + const ui::EventType event_type_; + + DISALLOW_COPY_AND_ASSIGN(CloseWidgetView); +}; + 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; + widget->IsActive() ? ui::SHOW_STATE_NORMAL : + ui::SHOW_STATE_INACTIVE; } 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); } //////////////////////////////////////////////////////////////////////////////// @@ -259,32 +267,6 @@ TEST_F(WidgetTest, Visibility) { // |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. // @@ -323,11 +305,11 @@ struct OwnershipTestState { // A platform NativeWidget subclass that updates a bag of state when it is // destroyed. -class OwnershipTestNativeWidget : public NativeWidgetPlatform { +class OwnershipTestNativeWidget : public PlatformNativeWidget { public: OwnershipTestNativeWidget(internal::NativeWidgetDelegate* delegate, OwnershipTestState* state) - : NativeWidgetPlatform(delegate), + : PlatformNativeWidget(delegate), state_(state) { } virtual ~OwnershipTestNativeWidget() { @@ -342,21 +324,21 @@ class OwnershipTestNativeWidget : public NativeWidgetPlatform { // A views NativeWidget subclass that updates a bag of state when it is // destroyed. -class OwnershipTestNativeWidgetPlatform : public NativeWidgetPlatformForTest { +class OwnershipTestNativeWidgetAura : public NativeWidgetCapture { public: - OwnershipTestNativeWidgetPlatform(internal::NativeWidgetDelegate* delegate, - OwnershipTestState* state) - : NativeWidgetPlatformForTest(delegate), + OwnershipTestNativeWidgetAura(internal::NativeWidgetDelegate* delegate, + OwnershipTestState* state) + : NativeWidgetCapture(delegate), state_(state) { } - virtual ~OwnershipTestNativeWidgetPlatform() { + virtual ~OwnershipTestNativeWidgetAura() { state_->native_widget_deleted = true; } private: OwnershipTestState* state_; - DISALLOW_COPY_AND_ASSIGN(OwnershipTestNativeWidgetPlatform); + DISALLOW_COPY_AND_ASSIGN(OwnershipTestNativeWidgetAura); }; // A Widget subclass that updates a bag of state when it is destroyed. @@ -381,7 +363,7 @@ TEST_F(WidgetOwnershipTest, Ownership_WidgetOwnsPlatformNativeWidget) { scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); params.native_widget = - new OwnershipTestNativeWidgetPlatform(widget.get(), &state); + new OwnershipTestNativeWidgetAura(widget.get(), &state); params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; widget->Init(params); @@ -402,7 +384,7 @@ TEST_F(WidgetOwnershipTest, Ownership_WidgetOwnsViewsNativeWidget) { scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); params.native_widget = - new OwnershipTestNativeWidgetPlatform(widget.get(), &state); + new OwnershipTestNativeWidgetAura(widget.get(), &state); params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; widget->Init(params); @@ -427,7 +409,7 @@ TEST_F(WidgetOwnershipTest, scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); params.native_widget = - new OwnershipTestNativeWidgetPlatform(widget.get(), &state); + new OwnershipTestNativeWidgetAura(widget.get(), &state); params.parent = toplevel->GetNativeView(); params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; widget->Init(params); @@ -457,7 +439,7 @@ TEST_F(WidgetOwnershipTest, Ownership_PlatformNativeWidgetOwnsWidget) { Widget* widget = new OwnershipTestWidget(&state); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); params.native_widget = - new OwnershipTestNativeWidgetPlatform(widget, &state); + new OwnershipTestNativeWidgetAura(widget, &state); widget->Init(params); // Now destroy the native widget. @@ -476,7 +458,7 @@ TEST_F(WidgetOwnershipTest, Ownership_ViewsNativeWidgetOwnsWidget) { Widget* widget = new OwnershipTestWidget(&state); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); params.native_widget = - new OwnershipTestNativeWidgetPlatform(widget, &state); + new OwnershipTestNativeWidgetAura(widget, &state); params.parent = toplevel->GetNativeView(); widget->Init(params); @@ -500,15 +482,11 @@ TEST_F(WidgetOwnershipTest, Widget* widget = new OwnershipTestWidget(&state); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); params.native_widget = - new OwnershipTestNativeWidgetPlatform(widget, &state); + new OwnershipTestNativeWidgetAura(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 + SimulateNativeDestroy(widget); EXPECT_TRUE(state.widget_deleted); EXPECT_TRUE(state.native_widget_deleted); @@ -525,7 +503,7 @@ TEST_F(WidgetOwnershipTest, Widget* widget = new OwnershipTestWidget(&state); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); params.native_widget = - new OwnershipTestNativeWidgetPlatform(widget, &state); + new OwnershipTestNativeWidgetAura(widget, &state); params.parent = toplevel->GetNativeView(); widget->Init(params); @@ -551,7 +529,7 @@ TEST_F(WidgetOwnershipTest, Widget* widget = new OwnershipTestWidget(&state); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); params.native_widget = - new OwnershipTestNativeWidgetPlatform(widget, &state); + new OwnershipTestNativeWidgetAura(widget, &state); params.parent = toplevel->GetNativeView(); widget->Init(params); @@ -577,7 +555,7 @@ TEST_F(WidgetOwnershipTest, scoped_ptr<Widget> widget(new OwnershipTestWidget(&state)); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); params.native_widget = - new OwnershipTestNativeWidgetPlatform(widget.get(), &state); + new OwnershipTestNativeWidgetAura(widget.get(), &state); params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params.delegate = delegate_view; widget->Init(params); @@ -614,7 +592,6 @@ class WidgetWithDestroyedNativeViewTest : public ViewsTestBase { widget->StackAtTop(); widget->IsClosed(); widget->Close(); - widget->Show(); widget->Hide(); widget->Activate(); widget->Deactivate(); @@ -646,11 +623,7 @@ class WidgetWithDestroyedNativeViewTest : public ViewsTestBase { widget->ReleaseCapture(); widget->HasCapture(); widget->GetWorkAreaBoundsInScreen(); - // These three crash with NativeWidgetWin, so I'm assuming we don't need - // them to work for the other NativeWidget impls. - // widget->CenterWindow(gfx::Size(50, 60)); - // widget->GetRestoredBounds(); - // widget->ShowInactive(); + widget->IsTranslucentWindowOpacitySupported(); } private: @@ -668,11 +641,11 @@ TEST_F(WidgetWithDestroyedNativeViewTest, Test) { widget.native_widget_private()->CloseNow(); InvokeWidgetMethods(&widget); } -#if defined(USE_AURA) && !defined(OS_CHROMEOS) +#if !defined(OS_CHROMEOS) { Widget widget; Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); - params.native_widget = new DesktopNativeWidgetAura(&widget); + params.native_widget = new PlatformDesktopNativeWidget(&widget); params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; widget.Init(params); widget.Show(); @@ -852,7 +825,7 @@ TEST_F(WidgetObserverTest, WidgetBoundsChanged) { EXPECT_EQ(child2, widget_bounds_changed()); } -#if !defined(USE_AURA) && defined(OS_WIN) +#if defined(false) // Aura needs shell to maximize/fullscreen window. // NativeWidgetGtk doesn't implement GetRestoredBounds. TEST_F(WidgetTest, GetRestoredBounds) { @@ -892,26 +865,21 @@ TEST_F(WidgetTest, ExitFullscreenRestoreState) { EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel)); toplevel->SetFullscreen(true); - while (GetWidgetShowState(toplevel) != ui::SHOW_STATE_FULLSCREEN) - RunPendingMessages(); + EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel)); toplevel->SetFullscreen(false); - while (GetWidgetShowState(toplevel) == ui::SHOW_STATE_FULLSCREEN) - RunPendingMessages(); + EXPECT_NE(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel)); // 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(); + EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel)); toplevel->SetFullscreen(true); - while (GetWidgetShowState(toplevel) != ui::SHOW_STATE_FULLSCREEN) - RunPendingMessages(); + EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel)); toplevel->SetFullscreen(false); - while (GetWidgetShowState(toplevel) == ui::SHOW_STATE_FULLSCREEN) - RunPendingMessages(); + EXPECT_NE(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel)); // And it stays maximized after getting out of full screen. EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel)); @@ -921,7 +889,6 @@ TEST_F(WidgetTest, ExitFullscreenRestoreState) { 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. @@ -930,7 +897,7 @@ TEST_F(WidgetTest, KeyboardInputEvent) { View* container = toplevel->client_view(); Textfield* textfield = new Textfield(); - textfield->SetText(ASCIIToUTF16("some text")); + textfield->SetText(base::ASCIIToUTF16("some text")); container->AddChildView(textfield); toplevel->Show(); textfield->RequestFocus(); @@ -959,7 +926,7 @@ TEST_F(WidgetTest, DISABLED_FocusChangesOnBubble) { 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); + init_params.native_widget = new PlatformDesktopNativeWidget(&widget); #endif widget.Init(init_params); widget.SetContentsView(contents_view); @@ -985,6 +952,36 @@ TEST_F(WidgetTest, DISABLED_FocusChangesOnBubble) { EXPECT_TRUE(contents_view->HasFocus()); } +class TestBubbleDelegateView : public BubbleDelegateView { + public: + TestBubbleDelegateView(View* anchor) + : BubbleDelegateView(anchor, BubbleBorder::NONE), + reset_controls_called_(false) {} + virtual ~TestBubbleDelegateView() {} + + virtual bool ShouldShowCloseButton() const OVERRIDE { + reset_controls_called_ = true; + return true; + } + + mutable bool reset_controls_called_; +}; + +TEST_F(WidgetTest, BubbleControlsResetOnInit) { + Widget* anchor = CreateTopLevelPlatformWidget(); + anchor->Show(); + + TestBubbleDelegateView* bubble_delegate = + new TestBubbleDelegateView(anchor->client_view()); + Widget* bubble_widget(BubbleDelegateView::CreateBubble(bubble_delegate)); + EXPECT_TRUE(bubble_delegate->reset_controls_called_); + bubble_widget->Show(); + bubble_widget->CloseNow(); + + anchor->Hide(); + anchor->CloseNow(); +} + // 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. @@ -997,7 +994,7 @@ TEST_F(WidgetTest, TestViewWidthAfterMinimizingWidget) { 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); + init_params.native_widget = new PlatformDesktopNativeWidget(&widget); widget.Init(init_params); NonClientView* non_client_view = widget.non_client_view(); NonClientFrameView* frame_view = new MinimumSizeFrameView(&widget); @@ -1051,7 +1048,7 @@ class DesktopAuraTestValidPaintWidget : public views::Widget { bool received_paint_while_hidden_; }; -TEST_F(WidgetTest, DesktopNativeWidgetAuraNoPaintAfterCloseTest) { +TEST_F(WidgetTest, DesktopNativeWidgetNoPaintAfterCloseTest) { View* contents_view = new View; contents_view->SetFocusable(true); DesktopAuraTestValidPaintWidget widget; @@ -1059,7 +1056,7 @@ TEST_F(WidgetTest, DesktopNativeWidgetAuraNoPaintAfterCloseTest) { 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); + init_params.native_widget = new PlatformDesktopNativeWidget(&widget); widget.Init(init_params); widget.SetContentsView(contents_view); widget.Show(); @@ -1071,7 +1068,7 @@ TEST_F(WidgetTest, DesktopNativeWidgetAuraNoPaintAfterCloseTest) { EXPECT_FALSE(widget.received_paint_while_hidden()); } -TEST_F(WidgetTest, DesktopNativeWidgetAuraNoPaintAfterHideTest) { +TEST_F(WidgetTest, DesktopNativeWidgetNoPaintAfterHideTest) { View* contents_view = new View; contents_view->SetFocusable(true); DesktopAuraTestValidPaintWidget widget; @@ -1079,7 +1076,7 @@ TEST_F(WidgetTest, DesktopNativeWidgetAuraNoPaintAfterHideTest) { 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); + init_params.native_widget = new PlatformDesktopNativeWidget(&widget); widget.Init(init_params); widget.SetContentsView(contents_view); widget.Show(); @@ -1092,170 +1089,6 @@ TEST_F(WidgetTest, DesktopNativeWidgetAuraNoPaintAfterHideTest) { widget.Close(); } -// This class provides functionality to create fullscreen and top level popup -// windows. It additionally tests whether the destruction of these 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 DesktopAuraTopLevelWindowTest - : public views::TestViewsDelegate, - public aura::WindowObserver { - public: - DesktopAuraTopLevelWindowTest() - : top_level_widget_(NULL), - owned_window_(NULL), - owner_destroyed_(false), - owned_window_destroyed_(false) {} - - virtual ~DesktopAuraTopLevelWindowTest() { - EXPECT_TRUE(owner_destroyed_); - EXPECT_TRUE(owned_window_destroyed_); - top_level_widget_ = NULL; - owned_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 CreateTopLevelWindow(const gfx::Rect& bounds, bool fullscreen) { - 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; - init_params.accept_events = fullscreen; - - widget_.Init(init_params); - - owned_window_ = new aura::Window(&child_window_delegate_); - owned_window_->SetType(aura::client::WINDOW_TYPE_NORMAL); - owned_window_->SetName("TestTopLevelWindow"); - if (fullscreen) { - owned_window_->SetProperty(aura::client::kShowStateKey, - ui::SHOW_STATE_FULLSCREEN); - } else { - owned_window_->SetType(aura::client::WINDOW_TYPE_MENU); - } - owned_window_->Init(ui::LAYER_TEXTURED); - aura::client::ParentWindowWithContext( - owned_window_, - widget_.GetNativeView()->GetRootWindow(), - gfx::Rect(0, 0, 1900, 1600)); - owned_window_->Show(); - owned_window_->AddObserver(this); - - ASSERT_TRUE(owned_window_->parent() != NULL); - owned_window_->parent()->AddObserver(this); - - top_level_widget_ = - views::Widget::GetWidgetForNativeView(owned_window_->parent()); - ASSERT_TRUE(top_level_widget_ != NULL); - } - - void DestroyOwnedWindow() { - ASSERT_TRUE(owned_window_ != NULL); - delete owned_window_; - } - - void DestroyOwnerWindow() { - ASSERT_TRUE(top_level_widget_ != NULL); - top_level_widget_->CloseNow(); - } - - virtual void OnWindowDestroying(aura::Window* window) OVERRIDE { - window->RemoveObserver(this); - if (window == owned_window_) { - owned_window_destroyed_ = true; - } else if (window == top_level_widget_->GetNativeView()) { - owner_destroyed_ = true; - } else { - ADD_FAILURE() << "Unexpected window destroyed callback: " << window; - } - } - - aura::Window* owned_window() { - return owned_window_; - } - - views::Widget* top_level_widget() { - return top_level_widget_; - } - - private: - views::Widget widget_; - views::Widget* top_level_widget_; - aura::Window* owned_window_; - bool owner_destroyed_; - bool owned_window_destroyed_; - aura::test::TestWindowDelegate child_window_delegate_; - - DISALLOW_COPY_AND_ASSIGN(DesktopAuraTopLevelWindowTest); -}; - -TEST_F(WidgetTest, DesktopAuraFullscreenWindowDestroyedBeforeOwnerTest) { - ViewsDelegate::views_delegate = NULL; - DesktopAuraTopLevelWindowTest fullscreen_window; - ASSERT_NO_FATAL_FAILURE(fullscreen_window.CreateTopLevelWindow( - gfx::Rect(0, 0, 200, 200), true)); - - RunPendingMessages(); - ASSERT_NO_FATAL_FAILURE(fullscreen_window.DestroyOwnedWindow()); - RunPendingMessages(); -} - -TEST_F(WidgetTest, DesktopAuraFullscreenWindowOwnerDestroyed) { - ViewsDelegate::views_delegate = NULL; - - DesktopAuraTopLevelWindowTest fullscreen_window; - ASSERT_NO_FATAL_FAILURE(fullscreen_window.CreateTopLevelWindow( - gfx::Rect(0, 0, 200, 200), true)); - - RunPendingMessages(); - ASSERT_NO_FATAL_FAILURE(fullscreen_window.DestroyOwnerWindow()); - RunPendingMessages(); -} - -TEST_F(WidgetTest, DesktopAuraTopLevelOwnedPopupTest) { - ViewsDelegate::views_delegate = NULL; - DesktopAuraTopLevelWindowTest popup_window; - ASSERT_NO_FATAL_FAILURE(popup_window.CreateTopLevelWindow( - gfx::Rect(0, 0, 200, 200), false)); - - RunPendingMessages(); - ASSERT_NO_FATAL_FAILURE(popup_window.DestroyOwnedWindow()); - RunPendingMessages(); -} - -#if defined(OS_WIN) -// TODO(ananta) -// Fix this test to work on Linux Aura. Need to implement the -// views::DesktopRootWindowHostX11::SetSize function -// This test validates that when a top level owned popup Aura window is -// resized, the widget is resized as well. -TEST_F(WidgetTest, DesktopAuraTopLevelOwnedPopupResizeTest) { - ViewsDelegate::views_delegate = NULL; - DesktopAuraTopLevelWindowTest popup_window; - ASSERT_NO_FATAL_FAILURE(popup_window.CreateTopLevelWindow( - gfx::Rect(0, 0, 200, 200), false)); - - gfx::Rect new_size(0, 0, 400, 400); - popup_window.owned_window()->SetBounds(new_size); - - EXPECT_EQ(popup_window.top_level_widget()->GetNativeView()->bounds().size(), - new_size.size()); - RunPendingMessages(); - ASSERT_NO_FATAL_FAILURE(popup_window.DestroyOwnedWindow()); - RunPendingMessages(); -} -#endif - // 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) { @@ -1267,16 +1100,16 @@ TEST_F(WidgetTest, TestWindowVisibilityAfterHide) { 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); + init_params.native_widget = new PlatformDesktopNativeWidget(&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()); + EXPECT_FALSE(IsNativeWindowVisible(widget.GetNativeWindow())); widget.Show(); - EXPECT_TRUE(widget.GetNativeView()->IsVisible()); + EXPECT_TRUE(IsNativeWindowVisible(widget.GetNativeWindow())); } // The following code verifies we can correctly destroy a Widget from a mouse @@ -1284,84 +1117,38 @@ TEST_F(WidgetTest, TestWindowVisibilityAfterHide) { // nested message loops from such events, nor has the code ever really dealt // with this situation. -// Class that closes the widget (which ends up deleting it immediately) when the -// appropriate event is received. -class CloseWidgetView : public View { - public: - explicit CloseWidgetView(ui::EventType event_type) - : event_type_(event_type) { - } - - // View overrides: - virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE { - if (!CloseWidget(event)) - View::OnMousePressed(event); - return true; - } - virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE { - if (!CloseWidget(event)) - View::OnMouseDragged(event); - return true; - } - virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE { - if (!CloseWidget(event)) - View::OnMouseReleased(event); - } - virtual void OnMouseMoved(const ui::MouseEvent& event) OVERRIDE { - if (!CloseWidget(event)) - View::OnMouseMoved(event); - } - virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE { - if (!CloseWidget(event)) - View::OnMouseEntered(event); - } - - private: - bool CloseWidget(const ui::LocatedEvent& event) { - if (event.type() == event_type_) { - // Go through NativeWidgetPrivate to simulate what happens if the OS - // deletes the NativeWindow out from under us. - GetWidget()->native_widget_private()->CloseNow(); - return true; - } - return false; - } - - const ui::EventType event_type_; - - DISALLOW_COPY_AND_ASSIGN(CloseWidgetView); -}; - // Generates two moves (first generates enter, second real move), a press, drag // and release stopping at |last_event_type|. void GenerateMouseEvents(Widget* widget, ui::EventType last_event_type) { const gfx::Rect screen_bounds(widget->GetWindowBoundsInScreen()); ui::MouseEvent move_event(ui::ET_MOUSE_MOVED, screen_bounds.CenterPoint(), - screen_bounds.CenterPoint(), 0); - aura::RootWindowHostDelegate* rwhd = - widget->GetNativeWindow()->GetDispatcher()->AsRootWindowHostDelegate(); - rwhd->OnHostMouseEvent(&move_event); - if (last_event_type == ui::ET_MOUSE_ENTERED) + screen_bounds.CenterPoint(), 0, 0); + ui::EventProcessor* dispatcher = WidgetTest::GetEventProcessor(widget); + ui::EventDispatchDetails details = dispatcher->OnEventFromSource(&move_event); + if (last_event_type == ui::ET_MOUSE_ENTERED || details.dispatcher_destroyed) return; - rwhd->OnHostMouseEvent(&move_event); - if (last_event_type == ui::ET_MOUSE_MOVED) + details = dispatcher->OnEventFromSource(&move_event); + if (last_event_type == ui::ET_MOUSE_MOVED || details.dispatcher_destroyed) return; ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, screen_bounds.CenterPoint(), - screen_bounds.CenterPoint(), 0); - rwhd->OnHostMouseEvent(&press_event); - if (last_event_type == ui::ET_MOUSE_PRESSED) + screen_bounds.CenterPoint(), 0, 0); + details = dispatcher->OnEventFromSource(&press_event); + if (last_event_type == ui::ET_MOUSE_PRESSED || details.dispatcher_destroyed) return; gfx::Point end_point(screen_bounds.CenterPoint()); end_point.Offset(1, 1); - ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, end_point, end_point, 0); - rwhd->OnHostMouseEvent(&drag_event); - if (last_event_type == ui::ET_MOUSE_DRAGGED) + ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, end_point, end_point, 0, 0); + details = dispatcher->OnEventFromSource(&drag_event); + if (last_event_type == ui::ET_MOUSE_DRAGGED || details.dispatcher_destroyed) return; - ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, end_point, end_point, 0); - rwhd->OnHostMouseEvent(&release_event); + ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, end_point, end_point, 0, + 0); + details = dispatcher->OnEventFromSource(&release_event); + if (details.dispatcher_destroyed) + return; } // Creates a widget and invokes GenerateMouseEvents() with |last_event_type|. @@ -1371,7 +1158,7 @@ void RunCloseWidgetDuringDispatchTest(WidgetTest* test, Widget* widget = new Widget; Widget::InitParams params = test->CreateParams(Widget::InitParams::TYPE_POPUP); - params.native_widget = new DesktopNativeWidgetAura(widget); + params.native_widget = new PlatformDesktopNativeWidget(widget); params.bounds = gfx::Rect(0, 0, 50, 100); widget->Init(params); widget->SetContentsView(new CloseWidgetView(last_event_type)); @@ -1430,8 +1217,6 @@ TEST_F(WidgetTest, WheelEventsFromScrollEventTarget) { 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) { @@ -1493,13 +1278,15 @@ TEST_F(WidgetTest, GestureScrollEventDispatching) { } // Tests that event-handlers installed on the RootView get triggered correctly. +// TODO(tdanderson): Clean up this test as part of crbug.com/355680. TEST_F(WidgetTest, EventHandlersOnRootView) { Widget* widget = CreateTopLevelNativeWidget(); View* root_view = widget->GetRootView(); - EventCountView* view = new EventCountView; + scoped_ptr<EventCountView> view(new EventCountView()); + view->set_owned_by_client(); view->SetBounds(0, 0, 20, 20); - root_view->AddChildView(view); + root_view->AddChildView(view.get()); EventCountHandler h1; root_view->AddPreTargetHandler(&h1); @@ -1510,16 +1297,6 @@ TEST_F(WidgetTest, EventHandlersOnRootView) { 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); @@ -1531,16 +1308,6 @@ TEST_F(WidgetTest, EventHandlersOnRootView) { 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)); @@ -1554,9 +1321,67 @@ TEST_F(WidgetTest, EventHandlersOnRootView) { 0, 20, 2); widget->OnScrollEvent(&scroll); - EXPECT_EQ(1, h1.GetEventCount(ui::ET_SCROLL)); + EXPECT_EQ(2, h1.GetEventCount(ui::ET_SCROLL)); EXPECT_EQ(1, view->GetEventCount(ui::ET_SCROLL)); - EXPECT_EQ(1, h2.GetEventCount(ui::ET_SCROLL)); + EXPECT_EQ(2, h2.GetEventCount(ui::ET_SCROLL)); + + // Unhandled scroll events are turned into wheel events and re-dispatched. + EXPECT_EQ(1, h1.GetEventCount(ui::ET_MOUSEWHEEL)); + EXPECT_EQ(1, view->GetEventCount(ui::ET_MOUSEWHEEL)); + EXPECT_EQ(1, h2.GetEventCount(ui::ET_MOUSEWHEEL)); + + h1.ResetCounts(); + view->ResetCounts(); + h2.ResetCounts(); + + ui::ScrollEvent fling(ui::ET_SCROLL_FLING_START, + gfx::Point(5, 5), + ui::EventTimeForNow(), + 0, + 0, 20, + 0, 20, + 2); + widget->OnScrollEvent(&fling); + EXPECT_EQ(2, h1.GetEventCount(ui::ET_SCROLL_FLING_START)); + EXPECT_EQ(1, view->GetEventCount(ui::ET_SCROLL_FLING_START)); + EXPECT_EQ(2, h2.GetEventCount(ui::ET_SCROLL_FLING_START)); + + // Unhandled scroll events which are not of type ui::ET_SCROLL should not + // be turned into wheel events and re-dispatched. + EXPECT_EQ(0, h1.GetEventCount(ui::ET_MOUSEWHEEL)); + EXPECT_EQ(0, view->GetEventCount(ui::ET_MOUSEWHEEL)); + EXPECT_EQ(0, h2.GetEventCount(ui::ET_MOUSEWHEEL)); + + h1.ResetCounts(); + view->ResetCounts(); + h2.ResetCounts(); + + // Replace the child of |root_view| with a ScrollableEventCountView so that + // ui::ET_SCROLL events are marked as handled at the target phase. + root_view->RemoveChildView(view.get()); + ScrollableEventCountView* scroll_view = new ScrollableEventCountView; + scroll_view->SetBounds(0, 0, 20, 20); + root_view->AddChildView(scroll_view); + + ui::ScrollEvent consumed_scroll(ui::ET_SCROLL, + gfx::Point(5, 5), + ui::EventTimeForNow(), + 0, + 0, 20, + 0, 20, + 2); + widget->OnScrollEvent(&consumed_scroll); + + // The event is handled at the target phase and should not reach the + // post-target handler. + EXPECT_EQ(1, h1.GetEventCount(ui::ET_SCROLL)); + EXPECT_EQ(1, scroll_view->GetEventCount(ui::ET_SCROLL)); + EXPECT_EQ(0, h2.GetEventCount(ui::ET_SCROLL)); + + // Handled scroll events are not turned into wheel events and re-dispatched. + EXPECT_EQ(0, h1.GetEventCount(ui::ET_MOUSEWHEEL)); + EXPECT_EQ(0, scroll_view->GetEventCount(ui::ET_MOUSEWHEEL)); + EXPECT_EQ(0, h2.GetEventCount(ui::ET_MOUSEWHEEL)); widget->CloseNow(); } @@ -1574,7 +1399,7 @@ TEST_F(WidgetTest, SynthesizeMouseMoveEvent) { gfx::Point cursor_location(5, 5); ui::MouseEvent move(ui::ET_MOUSE_MOVED, cursor_location, cursor_location, - ui::EF_NONE); + ui::EF_NONE, ui::EF_NONE); widget->OnMouseEvent(&move); EXPECT_EQ(1, v1->GetEventCount(ui::ET_MOUSE_ENTERED)); @@ -1621,8 +1446,8 @@ TEST_F(WidgetTest, SingleWindowClosing) { 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); +#if !defined(OS_CHROMEOS) + init_params.native_widget = new PlatformDesktopNativeWidget(widget); #endif widget->Init(init_params); EXPECT_EQ(0, delegate->count()); @@ -1638,9 +1463,9 @@ class WidgetWindowTitleTest : public WidgetTest { CreateParams(Widget::InitParams::TYPE_WINDOW); widget->Init(init_params); -#if defined(USE_AURA) && !defined(OS_CHROMEOS) +#if !defined(OS_CHROMEOS) if (desktop_native_widget) - init_params.native_widget = new DesktopNativeWidgetAura(widget); + init_params.native_widget = new PlatformDesktopNativeWidget(widget); #else DCHECK(!desktop_native_widget) << "DesktopNativeWidget does not exist on non-Aura or on ChromeOS."; @@ -1649,10 +1474,10 @@ class WidgetWindowTitleTest : public WidgetTest { internal::NativeWidgetPrivate* native_widget = widget->native_widget_private(); - string16 empty; - string16 s1(UTF8ToUTF16("Title1")); - string16 s2(UTF8ToUTF16("Title2")); - string16 s3(UTF8ToUTF16("TitleLong")); + base::string16 empty; + base::string16 s1(base::UTF8ToUTF16("Title1")); + base::string16 s2(base::UTF8ToUTF16("Title2")); + base::string16 s3(base::UTF8ToUTF16("TitleLong")); // The widget starts with no title, setting empty should not change // anything. @@ -1679,85 +1504,50 @@ TEST_F(WidgetWindowTitleTest, SetWindowTitleChanged_NativeWidget) { } // DesktopNativeWidget does not exist on non-Aura or on ChromeOS. -#if defined(USE_AURA) && !defined(OS_CHROMEOS) +#if !defined(OS_CHROMEOS) TEST_F(WidgetWindowTitleTest, SetWindowTitleChanged_DesktopNativeWidget) { // Override to use a DesktopNativeWidget. bool desktop_native_widget = true; RunTest(desktop_native_widget); } -#endif // USE_AURA && !OS_CHROMEOS +#endif // !OS_CHROMEOS -// 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); +TEST_F(WidgetTest, WidgetDeleted_InOnMousePressed) { + 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() {} + widget->SetContentsView(new CloseWidgetView(ui::ET_MOUSE_PRESSED)); - // Overridden from View. - virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE { - delete GetWidget(); - return true; - } + widget->SetSize(gfx::Size(100, 100)); + widget->Show(); - private: - DISALLOW_COPY_AND_ASSIGN(WidgetDeleterView); -}; + aura::test::EventGenerator generator(GetContext(), widget->GetNativeWindow()); -TEST_F(WidgetTest, TestWidgetDeletedInOnMousePressed) { + WidgetDeletionObserver deletion_observer(widget); + generator.ClickLeftButton(); + EXPECT_FALSE(deletion_observer.IsWidgetAlive()); + + // Yay we did not crash! +} + +TEST_F(WidgetTest, WidgetDeleted_InDispatchGestureEvent) { 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->SetContentsView(new CloseWidgetView(ui::ET_GESTURE_TAP_DOWN)); 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); + aura::test::EventGenerator generator(GetContext()); + + WidgetDeletionObserver deletion_observer(widget); + generator.GestureTapAt(widget->GetWindowBoundsInScreen().CenterPoint()); + EXPECT_FALSE(deletion_observer.IsWidgetAlive()); // Yay we did not crash! } @@ -1794,9 +1584,9 @@ bool RunGetNativeThemeFromDestructor(const Widget::InitParams& in_params, Widget::InitParams params(in_params); // Deletes itself when the Widget is destroyed. params.delegate = new GetNativeThemeFromDestructorView; -#if defined(USE_AURA) && !defined(OS_CHROMEOS) +#if !defined(OS_CHROMEOS) if (is_first_run) { - params.native_widget = new DesktopNativeWidgetAura(widget); + params.native_widget = new PlatformDesktopNativeWidget(widget); needs_second_run = true; } #endif @@ -1843,8 +1633,8 @@ TEST_F(WidgetTest, CloseDestroys) { 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); +#if !defined(OS_CHROMEOS) + params.native_widget = new PlatformDesktopNativeWidget(widget); #endif widget->Init(params); widget->Show(); @@ -1860,6 +1650,24 @@ TEST_F(WidgetTest, CloseDestroys) { } } +// Tests that killing a widget while animating it does not crash. +TEST_F(WidgetTest, CloseWidgetWhileAnimating) { + scoped_ptr<Widget> widget(new Widget); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget->Init(params); + + // Normal animations for tests have ZERO_DURATION, make sure we are actually + // animating the movement. + ui::ScopedAnimationDurationScaleMode animation_scale_mode( + ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); + ui::ScopedLayerAnimationSettings animation_settings( + widget->GetLayer()->GetAnimator()); + widget->Show(); + // Animate the bounds change. + widget->SetBounds(gfx::Rect(0, 0, 200, 200)); +} + // A view that consumes mouse-pressed event and gesture-tap-down events. class RootViewTestView : public View { public: @@ -1899,7 +1707,7 @@ TEST_F(WidgetTest, MAYBE_DisableTestRootViewHandlersWhenHidden) { 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); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); widget->OnMouseEvent(&press); EXPECT_EQ(view, GetMousePressedHandler(root_view)); widget->Hide(); @@ -1909,7 +1717,7 @@ TEST_F(WidgetTest, MAYBE_DisableTestRootViewHandlersWhenHidden) { 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); + ui::MouseEvent move(ui::ET_MOUSE_MOVED, move_location, move_location, 0, 0); widget->OnMouseEvent(&move); EXPECT_EQ(view, GetMouseMoveHandler(root_view)); widget->Hide(); @@ -1934,6 +1742,33 @@ TEST_F(WidgetTest, MAYBE_DisableTestRootViewHandlersWhenHidden) { widget->Close(); } +class GestureEndConsumerView : public View { + private: + virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { + if (event->type() == ui::ET_GESTURE_END) + event->SetHandled(); + } +}; + +TEST_F(WidgetTest, GestureHandlerNotSetOnGestureEnd) { + Widget* widget = CreateTopLevelNativeWidget(); + widget->SetBounds(gfx::Rect(0, 0, 300, 300)); + View* view = new GestureEndConsumerView(); + view->SetBounds(0, 0, 300, 300); + internal::RootView* root_view = + static_cast<internal::RootView*>(widget->GetRootView()); + root_view->AddChildView(view); + + widget->Show(); + EXPECT_EQ(NULL, GetGestureHandler(root_view)); + ui::GestureEvent end(ui::ET_GESTURE_END, 15, 15, 0, base::TimeDelta(), + ui::GestureEventDetails(ui::ET_GESTURE_END, 0, 0), 1); + widget->OnGestureEvent(&end); + EXPECT_EQ(NULL, GetGestureHandler(root_view)); + + widget->Close(); +} + // Test the result of Widget::GetAllChildWidgets(). TEST_F(WidgetTest, GetAllChildWidgets) { // Create the following widget hierarchy: @@ -2001,9 +1836,9 @@ class WidgetChildDestructionTest : public WidgetTest { Widget* top_level = new Widget; Widget::InitParams params = CreateParams(views::Widget::InitParams::TYPE_WINDOW); -#if defined(USE_AURA) && !defined(OS_CHROMEOS) +#if !defined(OS_CHROMEOS) if (top_level_has_desktop_native_widget_aura) - params.native_widget = new DesktopNativeWidgetAura(top_level); + params.native_widget = new PlatformDesktopNativeWidget(top_level); #endif top_level->Init(params); top_level->GetRootView()->AddChildView( @@ -2014,9 +1849,9 @@ class WidgetChildDestructionTest : public WidgetTest { Widget::InitParams child_params = CreateParams(views::Widget::InitParams::TYPE_POPUP); child_params.parent = top_level->GetNativeView(); -#if defined(USE_AURA) && !defined(OS_CHROMEOS) +#if !defined(OS_CHROMEOS) if (child_has_desktop_native_widget_aura) - child_params.native_widget = new DesktopNativeWidgetAura(child); + child_params.native_widget = new PlatformDesktopNativeWidget(child); #endif child->Init(child_params); child->GetRootView()->AddChildView( @@ -2036,7 +1871,7 @@ class WidgetChildDestructionTest : public WidgetTest { DISALLOW_COPY_AND_ASSIGN(WidgetChildDestructionTest); }; -#if defined(USE_AURA) && !defined(OS_CHROMEOS) +#if !defined(OS_CHROMEOS) // See description of RunDestroyChildWidgetsTest(). Parent uses // DesktopNativeWidgetAura. TEST_F(WidgetChildDestructionTest, @@ -2050,14 +1885,14 @@ TEST_F(WidgetChildDestructionTest, DestroyChildWidgetsInOrderWithDesktopNativeWidgetForBoth) { RunDestroyChildWidgetsTest(true, true); } -#endif +#endif // !defined(OS_CHROMEOS) // See description of RunDestroyChildWidgetsTest(). TEST_F(WidgetChildDestructionTest, DestroyChildWidgetsInOrder) { RunDestroyChildWidgetsTest(false, false); } -#if defined(USE_AURA) && !defined(OS_CHROMEOS) +#if !defined(OS_CHROMEOS) // Provides functionality to create a window modal dialog. class ModalDialogDelegate : public DialogDelegateView { public: @@ -2084,7 +1919,8 @@ TEST_F(WidgetTest, WindowMouseModalityTest) { gfx::Rect initial_bounds(0, 0, 500, 500); init_params.bounds = initial_bounds; init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - init_params.native_widget = new DesktopNativeWidgetAura(&top_level_widget); + init_params.native_widget = + new PlatformDesktopNativeWidget(&top_level_widget); top_level_widget.Init(init_params); top_level_widget.Show(); EXPECT_TRUE(top_level_widget.IsVisible()); @@ -2098,9 +1934,11 @@ TEST_F(WidgetTest, WindowMouseModalityTest) { ui::MouseEvent move_main(ui::ET_MOUSE_MOVED, cursor_location_main, cursor_location_main, + ui::EF_NONE, ui::EF_NONE); - top_level_widget.GetNativeView()->GetDispatcher()-> - AsRootWindowHostDelegate()->OnHostMouseEvent(&move_main); + ui::EventDispatchDetails details = + GetEventProcessor(&top_level_widget)->OnEventFromSource(&move_main); + ASSERT_FALSE(details.dispatcher_destroyed); EXPECT_EQ(1, widget_view->GetEventCount(ui::ET_MOUSE_ENTERED)); widget_view->ResetCounts(); @@ -2112,7 +1950,7 @@ TEST_F(WidgetTest, WindowMouseModalityTest) { ModalDialogDelegate* dialog_delegate = new ModalDialogDelegate; Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget( - dialog_delegate, NULL, top_level_widget.GetNativeWindow()); + dialog_delegate, NULL, top_level_widget.GetNativeView()); modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200)); EventCountView* dialog_widget_view = new EventCountView(); dialog_widget_view->SetBounds(0, 0, 50, 50); @@ -2124,9 +1962,11 @@ TEST_F(WidgetTest, WindowMouseModalityTest) { ui::MouseEvent mouse_down_dialog(ui::ET_MOUSE_PRESSED, cursor_location_dialog, cursor_location_dialog, + ui::EF_NONE, ui::EF_NONE); - top_level_widget.GetNativeView()->GetDispatcher()-> - AsRootWindowHostDelegate()->OnHostMouseEvent(&mouse_down_dialog); + details = GetEventProcessor(&top_level_widget)->OnEventFromSource( + &mouse_down_dialog); + ASSERT_FALSE(details.dispatcher_destroyed); EXPECT_EQ(1, dialog_widget_view->GetEventCount(ui::ET_MOUSE_PRESSED)); // Send a mouse move message to the main window. It should not be received by @@ -2135,16 +1975,17 @@ TEST_F(WidgetTest, WindowMouseModalityTest) { ui::MouseEvent mouse_down_main(ui::ET_MOUSE_MOVED, cursor_location_main2, cursor_location_main2, + ui::EF_NONE, ui::EF_NONE); - top_level_widget.GetNativeView()->GetDispatcher()-> - AsRootWindowHostDelegate()->OnHostMouseEvent(&mouse_down_main); + details = GetEventProcessor(&top_level_widget)->OnEventFromSource( + &mouse_down_main); + ASSERT_FALSE(details.dispatcher_destroyed); EXPECT_EQ(0, widget_view->GetEventCount(ui::ET_MOUSE_MOVED)); modal_dialog_widget->CloseNow(); top_level_widget.CloseNow(); } -#if defined(USE_AURA) // Verifies nativeview visbility matches that of Widget visibility when // SetFullscreen is invoked. TEST_F(WidgetTest, FullscreenStatePropagated) { @@ -2159,23 +2000,23 @@ TEST_F(WidgetTest, FullscreenStatePropagated) { top_level_widget.Init(init_params); top_level_widget.SetFullscreen(true); EXPECT_EQ(top_level_widget.IsVisible(), - top_level_widget.GetNativeView()->IsVisible()); + IsNativeWindowVisible(top_level_widget.GetNativeWindow())); top_level_widget.CloseNow(); } #if !defined(OS_CHROMEOS) { Widget top_level_widget; - init_params.native_widget = new DesktopNativeWidgetAura(&top_level_widget); + init_params.native_widget = + new PlatformDesktopNativeWidget(&top_level_widget); top_level_widget.Init(init_params); top_level_widget.SetFullscreen(true); EXPECT_EQ(top_level_widget.IsVisible(), - top_level_widget.GetNativeView()->IsVisible()); + IsNativeWindowVisible(top_level_widget.GetNativeWindow())); top_level_widget.CloseNow(); } #endif } -#endif #if defined(OS_WIN) @@ -2268,8 +2109,67 @@ TEST_F(WidgetTest, WindowModalityActivationTest) { modal_dialog_widget->CloseNow(); top_level_widget.CloseNow(); } -#endif -#endif +#endif // defined(OS_WIN) +#endif // !defined(OS_CHROMEOS) + +TEST_F(WidgetTest, ShowCreatesActiveWindow) { + Widget* widget = CreateTopLevelPlatformWidget(); + + widget->Show(); + EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL); + + widget->CloseNow(); +} + +TEST_F(WidgetTest, ShowInactive) { + Widget* widget = CreateTopLevelPlatformWidget(); + + widget->ShowInactive(); + EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_INACTIVE); + + widget->CloseNow(); +} + +TEST_F(WidgetTest, ShowInactiveAfterShow) { + Widget* widget = CreateTopLevelPlatformWidget(); + + widget->Show(); + widget->ShowInactive(); + EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL); + + widget->CloseNow(); +} + +TEST_F(WidgetTest, ShowAfterShowInactive) { + Widget* widget = CreateTopLevelPlatformWidget(); + + widget->ShowInactive(); + widget->Show(); + EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL); + + widget->CloseNow(); +} + +#if !defined(OS_CHROMEOS) +TEST_F(WidgetTest, InactiveWidgetDoesNotGrabActivation) { + Widget* widget = CreateTopLevelPlatformWidget(); + widget->Show(); + EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL); + + Widget widget2; + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.native_widget = new PlatformDesktopNativeWidget(&widget2); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget2.Init(params); + widget2.Show(); + + EXPECT_EQ(GetWidgetShowState(&widget2), ui::SHOW_STATE_INACTIVE); + EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL); + + widget->CloseNow(); + widget2.CloseNow(); +} +#endif // !defined(OS_CHROMEOS) namespace { @@ -2359,7 +2259,7 @@ TEST_F(WidgetTest, IsActiveFromDestroy) { Widget parent_widget; Widget::InitParams parent_params = CreateParams(Widget::InitParams::TYPE_POPUP); - parent_params.native_widget = new DesktopNativeWidgetAura(&parent_widget); + parent_params.native_widget = new PlatformDesktopNativeWidget(&parent_widget); parent_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; parent_widget.Init(parent_params); parent_widget.Show(); @@ -2375,7 +2275,7 @@ TEST_F(WidgetTest, IsActiveFromDestroy) { parent_widget.CloseNow(); } -#endif +#endif // !defined(OS_CHROMEOS) } // namespace test } // namespace views diff --git a/chromium/ui/views/widget/window_reorderer_unittest.cc b/chromium/ui/views/widget/window_reorderer_unittest.cc index 1d9566c916e..67cf246e73a 100644 --- a/chromium/ui/views/widget/window_reorderer_unittest.cc +++ b/chromium/ui/views/widget/window_reorderer_unittest.cc @@ -2,10 +2,10 @@ // 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/aura/window_event_dispatcher.h" #include "ui/compositor/layer.h" #include "ui/compositor/test/test_layers.h" #include "ui/views/view.h" diff --git a/chromium/ui/views/win/DEPS b/chromium/ui/views/win/DEPS index 2365859be11..8cab1708ab1 100644 --- a/chromium/ui/views/win/DEPS +++ b/chromium/ui/views/win/DEPS @@ -19,9 +19,7 @@ include_rules = [ "+ui/views/ime/input_method_delegate.h", "+ui/views/views_delegate.h", "+ui/views/views_export.h", - "+ui/views/widget/child_window_message_processor.h", "+ui/views/widget/monitor_win.h", - "+ui/views/widget/native_widget_win.h", "+ui/views/widget/widget_hwnd_utils.h", "+ui/views/win", ] diff --git a/chromium/ui/views/win/appbar.cc b/chromium/ui/views/win/appbar.cc deleted file mode 100644 index 92151d47756..00000000000 --- a/chromium/ui/views/win/appbar.cc +++ /dev/null @@ -1,88 +0,0 @@ -// 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/win/appbar.h" - -#include <shellapi.h> - -#include "base/auto_reset.h" -#include "base/bind.h" -#include "base/location.h" -#include "base/threading/worker_pool.h" -#include "base/win/scoped_com_initializer.h" -#include "ui/views/widget/monitor_win.h" - -namespace views { - -namespace { - -void GetEdgesOnWorkerThread(HMONITOR monitor, int* edge) { - base::win::ScopedCOMInitializer com_initializer; - *edge = 0; - if (GetTopmostAutoHideTaskbarForEdge(ABE_LEFT, monitor)) - *edge = Appbar::EDGE_LEFT; - if (GetTopmostAutoHideTaskbarForEdge(ABE_TOP, monitor)) - *edge = Appbar::EDGE_TOP; - if (GetTopmostAutoHideTaskbarForEdge(ABE_RIGHT, monitor)) - *edge = Appbar::EDGE_RIGHT; - if (GetTopmostAutoHideTaskbarForEdge(ABE_BOTTOM, monitor)) - *edge = Appbar::EDGE_BOTTOM; -} - -} - -// static -Appbar* Appbar::instance() { - static Appbar* appbar = NULL; - if (!appbar) - appbar = new Appbar(); - return appbar; -} - -int Appbar::GetAutohideEdges(HMONITOR monitor, const base::Closure& callback) { - // Initialize the map with EDGE_BOTTOM. This is important, as if we return an - // initial value of 0 (no auto-hide edges) then we'll go fullscreen and - // windows will automatically remove WS_EX_TOPMOST from the appbar resulting - // in us thinking there is no auto-hide edges. By returning at least one edge - // we don't initially go fullscreen until we figure out the real auto-hide - // edges. - if (edge_map_.find(monitor) == edge_map_.end()) - edge_map_[monitor] = Appbar::EDGE_BOTTOM; - if (!in_callback_) { - int* edge = new int; - base::WorkerPool::PostTaskAndReply( - FROM_HERE, - base::Bind(&GetEdgesOnWorkerThread, - monitor, - base::Unretained(edge)), - base::Bind(&Appbar::OnGotEdges, - weak_factory_.GetWeakPtr(), - callback, - monitor, - edge_map_[monitor], - base::Owned(edge)), - false); - } - return edge_map_[monitor]; -} - -Appbar::Appbar() : weak_factory_(this), in_callback_(false) { -} - -Appbar::~Appbar() { -} - -void Appbar::OnGotEdges(const base::Closure& callback, - HMONITOR monitor, - int returned_edges, - int* edges) { - edge_map_[monitor] = *edges; - if (returned_edges == *edges) - return; - - base::AutoReset<bool> in_callback_setter(&in_callback_, true); - callback.Run(); -} - -} // namespace views diff --git a/chromium/ui/views/win/appbar.h b/chromium/ui/views/win/appbar.h deleted file mode 100644 index 4c24c192548..00000000000 --- a/chromium/ui/views/win/appbar.h +++ /dev/null @@ -1,70 +0,0 @@ -// 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_WIN_APPBAR_H_ -#define UI_VIEWS_WIN_APPBAR_H_ - -#include <map> - -#include <windows.h> - -#include "base/basictypes.h" -#include "base/callback_forward.h" -#include "base/memory/weak_ptr.h" - -namespace views { - -// Appbar provides an API to query for the edges of the monitor that have an -// autohide bar. -// NOTE: querying is done on a separate thread as it spawns a nested message -// loop. The nested message loop is particularly problematic here as it's -// possible for the nested message loop to run during window creation and -// startup time (WM_NCCALCSIZE is called at creation time). -class Appbar { - public: - enum Edge { - EDGE_TOP = 1 << 0, - EDGE_LEFT = 1 << 1, - EDGE_BOTTOM = 1 << 2, - EDGE_RIGHT = 1 << 3, - }; - - // Returns the singleton instance. - static Appbar* instance(); - - // Starts a query for the autohide edges of the specified monitor and returns - // the current value. If the edges have changed |callback| is subsequently - // invoked. If the edges have not changed |callback| is never run. - // - // Return value is a bitmask of Edges. - int GetAutohideEdges(HMONITOR monitor, const base::Closure& callback); - - private: - typedef std::map<HMONITOR, int> EdgeMap; - - Appbar(); - ~Appbar(); - - // Callback on main thread with the edges. |returned_edges| is the value that - // was returned from the call to GetAutohideEdges() that initiated the lookup. - void OnGotEdges(const base::Closure& callback, - HMONITOR monitor, - int returned_edges, - int* edges); - - EdgeMap edge_map_; - - base::WeakPtrFactory<Appbar> weak_factory_; - - // If true we're in the process of notifying a callback. When true we do not - // start a new query. - bool in_callback_; - - DISALLOW_COPY_AND_ASSIGN(Appbar); -}; - -} // namespace views - -#endif // UI_VIEWS_WIN_APPBAR_H_ - diff --git a/chromium/ui/views/win/fullscreen_handler.cc b/chromium/ui/views/win/fullscreen_handler.cc index 2fe8ceaf2b5..a6f51567856 100644 --- a/chromium/ui/views/win/fullscreen_handler.cc +++ b/chromium/ui/views/win/fullscreen_handler.cc @@ -76,8 +76,8 @@ void FullscreenHandler::SetFullscreenImpl(bool fullscreen, bool for_metro) { if (!for_metro) { MONITORINFO monitor_info; monitor_info.cbSize = sizeof(monitor_info); - base::win::GetMonitorInfoWrapper( - MonitorFromWindow(hwnd_, MONITOR_DEFAULTTONEAREST), &monitor_info); + GetMonitorInfo(MonitorFromWindow(hwnd_, MONITOR_DEFAULTTONEAREST), + &monitor_info); gfx::Rect window_rect(monitor_info.rcMonitor); SetWindowPos(hwnd_, NULL, window_rect.x(), window_rect.y(), window_rect.width(), window_rect.height(), diff --git a/chromium/ui/views/win/hwnd_message_handler.cc b/chromium/ui/views/win/hwnd_message_handler.cc index ab43f1224ff..adbe4fabe69 100644 --- a/chromium/ui/views/win/hwnd_message_handler.cc +++ b/chromium/ui/views/win/hwnd_message_handler.cc @@ -5,6 +5,7 @@ #include "ui/views/win/hwnd_message_handler.h" #include <dwmapi.h> +#include <oleacc.h> #include <shellapi.h> #include <wtsapi32.h> #pragma comment(lib, "wtsapi32.lib") @@ -14,6 +15,8 @@ #include "base/win/win_util.h" #include "base/win/windows_version.h" #include "ui/base/touch/touch_enabled.h" +#include "ui/base/view_prop.h" +#include "ui/base/win/internal_constants.h" #include "ui/base/win/lock_state.h" #include "ui/base/win/mouse_wheel_util.h" #include "ui/base/win/shell.h" @@ -34,18 +37,11 @@ #include "ui/native_theme/native_theme_win.h" #include "ui/views/views_delegate.h" #include "ui/views/widget/monitor_win.h" -#include "ui/views/widget/native_widget_win.h" #include "ui/views/widget/widget_hwnd_utils.h" -#include "ui/views/win/appbar.h" #include "ui/views/win/fullscreen_handler.h" #include "ui/views/win/hwnd_message_handler_delegate.h" #include "ui/views/win/scoped_fullscreen_visibility.h" -#if !defined(USE_AURA) -#include "ui/views/accessibility/native_view_accessibility_win.h" -#include "ui/views/widget/child_window_message_processor.h" -#endif - namespace views { namespace { @@ -186,7 +182,7 @@ bool GetMonitorAndRects(const RECT& rect, return false; MONITORINFO monitor_info = { 0 }; monitor_info.cbSize = sizeof(monitor_info); - base::win::GetMonitorInfoWrapper(*monitor, &monitor_info); + GetMonitorInfo(*monitor, &monitor_info); *monitor_rect = gfx::Rect(monitor_info.rcMonitor); *work_area = gfx::Rect(monitor_info.rcWork); return true; @@ -197,19 +193,6 @@ struct FindOwnedWindowsData { std::vector<Widget*> owned_widgets; }; -BOOL CALLBACK FindOwnedWindowsCallback(HWND hwnd, LPARAM param) { - // TODO(beng): resolve wrt aura. -#if !defined(USE_AURA) - FindOwnedWindowsData* data = reinterpret_cast<FindOwnedWindowsData*>(param); - if (GetWindow(hwnd, GW_OWNER) == data->window) { - Widget* widget = Widget::GetWidgetForNativeView(hwnd); - if (widget) - data->owned_widgets.push_back(widget); - } -#endif - return TRUE; -} - // Enables or disables the menu item for the specified command and menu. void EnableMenuItemByCommand(HMENU menu, UINT command, bool enabled) { UINT flags = MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_DISABLED | MF_GRAYED); @@ -251,54 +234,6 @@ static BOOL CALLBACK ClipDCToChild(HWND window, LPARAM param) { return TRUE; } -#if !defined(USE_AURA) - -// Get the source HWND of the specified message. Depending on the message, the -// source HWND is encoded in either the WPARAM or the LPARAM value. -HWND GetControlHWNDForMessage(UINT message, WPARAM w_param, LPARAM l_param) { - // Each of the following messages can be sent by a child HWND and must be - // forwarded to its associated NativeControlWin for handling. - switch (message) { - case WM_NOTIFY: - return reinterpret_cast<NMHDR*>(l_param)->hwndFrom; - case WM_COMMAND: - return reinterpret_cast<HWND>(l_param); - case WM_CONTEXTMENU: - return reinterpret_cast<HWND>(w_param); - case WM_CTLCOLORBTN: - case WM_CTLCOLORSTATIC: - return reinterpret_cast<HWND>(l_param); - } - return NULL; -} - -// Some messages may be sent to us by a child HWND. If this is the case, this -// function will forward those messages on to the object associated with the -// source HWND and return true, in which case the window procedure must not do -// any further processing of the message. If there is no associated -// ChildWindowMessageProcessor, the return value will be false and the WndProc -// can continue processing the message normally. |l_result| contains the result -// of the message processing by the control and must be returned by the WndProc -// if the return value is true. -bool ProcessChildWindowMessage(UINT message, - WPARAM w_param, - LPARAM l_param, - LRESULT* l_result) { - *l_result = 0; - - HWND control_hwnd = GetControlHWNDForMessage(message, w_param, l_param); - if (IsWindow(control_hwnd)) { - ChildWindowMessageProcessor* processor = - ChildWindowMessageProcessor::Get(control_hwnd); - if (processor) - return processor->ProcessMessage(message, w_param, l_param, l_result); - } - - return false; -} - -#endif - // The thickness of an auto-hide taskbar in pixels. const int kAutoHideTaskbarThicknessPx = 2; @@ -318,6 +253,15 @@ void AddScrollStylesToWindow(HWND window) { } } +const int kTouchDownContextResetTimeout = 500; + +// Windows does not flag synthesized mouse messages from touch in all cases. +// This causes us grief as we don't want to process touch and mouse messages +// concurrently. Hack as per msdn is to check if the time difference between +// the touch message and the mouse move is within 500 ms and at the same +// location as the cursor. +const int kSynthesizedMouseTouchMessagesTimeDifference = 500; + } // namespace // A scoping class that prevents a window from being able to redraw in response @@ -387,6 +331,8 @@ class HWNDMessageHandler::ScopedRedrawLock { //////////////////////////////////////////////////////////////////////////////// // HWNDMessageHandler, public: +long HWNDMessageHandler::last_touch_message_time_ = 0; + HWNDMessageHandler::HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate) : delegate_(delegate), fullscreen_handler_(new FullscreenHandler), @@ -394,7 +340,6 @@ HWNDMessageHandler::HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate) waiting_for_close_now_(false), remove_standard_frame_(false), use_system_default_icon_(false), - restore_focus_when_enabled_(false), restored_enabled_(false), current_cursor_(NULL), previous_cursor_(NULL), @@ -407,10 +352,14 @@ HWNDMessageHandler::HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate) layered_alpha_(255), waiting_for_redraw_layered_window_contents_(false), is_first_nccalc_(true), + menu_depth_(0), autohide_factory_(this), id_generator_(0), needs_scroll_styles_(false), - in_size_loop_(false) { + in_size_loop_(false), + touch_down_contexts_(0), + last_mouse_hwheel_time_(0), + msg_handled_(FALSE) { } HWNDMessageHandler::~HWNDMessageHandler() { @@ -427,8 +376,9 @@ void HWNDMessageHandler::Init(HWND parent, const gfx::Rect& bounds) { // Create the window. WindowImpl::Init(parent, bounds); - -#if defined(USE_AURA) + // TODO(ananta) + // Remove the scrolling hack code once we have scrolling working well. +#if defined(ENABLE_SCROLL_HACK) // Certain trackpad drivers on Windows have bugs where in they don't generate // WM_MOUSEWHEEL messages for the trackpoint and trackpad scrolling gestures // unless there is an entry for Chrome with the class name of the Window. @@ -448,6 +398,10 @@ void HWNDMessageHandler::Init(HWND parent, const gfx::Rect& bounds) { } } #endif + + prop_window_target_.reset(new ui::ViewProp(hwnd(), + ui::WindowEventTarget::kWin32InputEventTarget, + static_cast<ui::WindowEventTarget*>(this))); } void HWNDMessageHandler::InitModalType(ui::ModalType modal_type) { @@ -549,7 +503,7 @@ void HWNDMessageHandler::GetWindowPlacement( } else { MONITORINFO mi; mi.cbSize = sizeof(mi); - const bool succeeded = base::win::GetMonitorInfoWrapper( + const bool succeeded = GetMonitorInfo( MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONEAREST), &mi) != 0; DCHECK(succeeded); @@ -571,13 +525,24 @@ void HWNDMessageHandler::GetWindowPlacement( } } -void HWNDMessageHandler::SetBounds(const gfx::Rect& bounds_in_pixels) { +void HWNDMessageHandler::SetBounds(const gfx::Rect& bounds_in_pixels, + bool force_size_changed) { LONG style = GetWindowLong(hwnd(), GWL_STYLE); if (style & WS_MAXIMIZE) SetWindowLong(hwnd(), GWL_STYLE, style & ~WS_MAXIMIZE); + + gfx::Size old_size = GetClientAreaBounds().size(); SetWindowPos(hwnd(), NULL, bounds_in_pixels.x(), bounds_in_pixels.y(), bounds_in_pixels.width(), bounds_in_pixels.height(), SWP_NOACTIVATE | SWP_NOZORDER); + + // If HWND size is not changed, we will not receive standard size change + // notifications. If |force_size_changed| is |true|, we should pretend size is + // changed. + if (old_size == bounds_in_pixels.size() && force_size_changed) { + delegate_->HandleClientSizeChanged(GetClientAreaBounds().size()); + ResetWindowRegion(false, true); + } } void HWNDMessageHandler::SetSize(const gfx::Size& size) { @@ -609,8 +574,14 @@ void HWNDMessageHandler::StackAtTop() { } void HWNDMessageHandler::Show() { - if (IsWindow(hwnd())) - ShowWindowWithState(ui::SHOW_STATE_INACTIVE); + if (IsWindow(hwnd())) { + if (!(GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_TRANSPARENT) && + !(GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)) { + ShowWindowWithState(ui::SHOW_STATE_NORMAL); + } else { + ShowWindowWithState(ui::SHOW_STATE_INACTIVE); + } + } } void HWNDMessageHandler::ShowWindowWithState(ui::WindowShowState show_state) { @@ -630,30 +601,28 @@ void HWNDMessageHandler::ShowWindowWithState(ui::WindowShowState show_state) { native_show_state = delegate_->GetInitialShowState(); break; } - Show(native_show_state); -} -void HWNDMessageHandler::Show(int show_state) { - ShowWindow(hwnd(), show_state); + ShowWindow(hwnd(), native_show_state); // When launched from certain programs like bash and Windows Live Messenger, // show_state is set to SW_HIDE, so we need to correct that condition. We // don't just change show_state to SW_SHOWNORMAL because MSDN says we must // always first call ShowWindow with the specified value from STARTUPINFO, // otherwise all future ShowWindow calls will be ignored (!!#@@#!). Instead, // we call ShowWindow again in this case. - if (show_state == SW_HIDE) { - show_state = SW_SHOWNORMAL; - ShowWindow(hwnd(), show_state); + if (native_show_state == SW_HIDE) { + native_show_state = SW_SHOWNORMAL; + ShowWindow(hwnd(), native_show_state); } // We need to explicitly activate the window if we've been shown with a state // that should activate, because if we're opened from a desktop shortcut while // an existing window is already running it doesn't seem to be enough to use // one of these flags to activate the window. - if (show_state == SW_SHOWNORMAL || show_state == SW_SHOWMAXIMIZED) + if (native_show_state == SW_SHOWNORMAL || + native_show_state == SW_SHOWMAXIMIZED) Activate(); - if (!delegate_->HandleInitialFocus()) + if (!delegate_->HandleInitialFocus(show_state)) SetInitialFocus(); } @@ -674,9 +643,6 @@ void HWNDMessageHandler::Hide() { SetWindowPos(hwnd(), NULL, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER); - - if (!GetParent(hwnd())) - NotifyOwnedWindowsParentClosing(); } } @@ -740,12 +706,11 @@ bool HWNDMessageHandler::RunMoveLoop(const gfx::Vector2d& drag_offset, bool hide_on_escape) { ReleaseCapture(); MoveLoopMouseWatcher watcher(this, hide_on_escape); -#if defined(USE_AURA) // In Aura, we handle touch events asynchronously. So we need to allow nested // tasks while in windows move loop. base::MessageLoop::ScopedNestableTaskAllower allow_nested( base::MessageLoop::current()); -#endif + SendMessage(hwnd(), WM_SYSCOMMAND, SC_MOVE | 0x0002, GetMessagePos()); // Windows doesn't appear to offer a way to determine whether the user // canceled the move or not. We assume if the user released the mouse it was @@ -804,8 +769,8 @@ void HWNDMessageHandler::SetVisibilityChangedAnimationsEnabled(bool enabled) { } } -bool HWNDMessageHandler::SetTitle(const string16& title) { - string16 current_title; +bool HWNDMessageHandler::SetTitle(const base::string16& title) { + base::string16 current_title; size_t len_with_null = GetWindowTextLength(hwnd()) + 1; if (len_with_null == 1 && title.length() == 0) return false; @@ -831,6 +796,7 @@ void HWNDMessageHandler::SetCursor(HCURSOR cursor) { void HWNDMessageHandler::FrameTypeChanged() { // Called when the frame type could possibly be changing (theme change or // DWM composition change). + UpdateDwmNcRenderingPolicy(); // Don't redraw the window here, because we need to hide and show the window // which will also trigger a redraw. @@ -849,8 +815,9 @@ void HWNDMessageHandler::FrameTypeChanged() { UINT flags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER; SetWindowPos(hwnd(), NULL, 0, 0, 0, 0, flags | SWP_HIDEWINDOW); SetWindowPos(hwnd(), NULL, 0, 0, 0, 0, flags | SWP_SHOWWINDOW); - - UpdateWindow(hwnd()); + // Invalidate the window to force a paint. There may be child windows which + // could resize in this context. Don't paint right away. + ::InvalidateRect(hwnd(), NULL, FALSE); } // WM_DWMCOMPOSITIONCHANGED is only sent to top level windows, however we want @@ -937,14 +904,6 @@ LRESULT HWNDMessageHandler::OnWndProc(UINT message, if (delegate_ && delegate_->PreHandleMSG(message, w_param, l_param, &result)) return result; -#if !defined(USE_AURA) - // First allow messages sent by child controls to be processed directly by - // their associated views. If such a view is present, it will handle the - // message *instead of* this NativeWidgetWin. - if (ProcessChildWindowMessage(message, w_param, l_param, &result)) - return result; -#endif - // Otherwise we handle everything else. // NOTE: We inline ProcessWindowMessage() as 'this' may be destroyed during // dispatch and ProcessWindowMessage() doesn't deal with that well. @@ -963,40 +922,48 @@ LRESULT HWNDMessageHandler::OnWndProc(UINT message, if (!::IsWindow(window)) return result; - if (delegate_) + if (delegate_) { delegate_->PostHandleMSG(message, w_param, l_param); - if (message == WM_NCDESTROY) { -#if !defined(USE_AURA) - base::MessageLoopForUI::current()->RemoveObserver(this); -#endif - if (delegate_) + if (message == WM_NCDESTROY) delegate_->HandleDestroyed(); } - // Only top level widget should store/restore focus. - if (message == WM_ACTIVATE && delegate_->CanSaveFocus()) + if (message == WM_ACTIVATE && IsTopLevelWindow(window)) PostProcessActivateMessage(LOWORD(w_param), !!HIWORD(w_param)); - - if (message == WM_ENABLE && restore_focus_when_enabled_) { - // This path should be executed only for top level as - // restore_focus_when_enabled_ is set in PostProcessActivateMessage. - DCHECK(delegate_->CanSaveFocus()); - restore_focus_when_enabled_ = false; - delegate_->RestoreFocusOnEnable(); - } return result; } -//////////////////////////////////////////////////////////////////////////////// -// HWNDMessageHandler, MessageLoopForUI::Observer implementation: +LRESULT HWNDMessageHandler::HandleMouseMessage(unsigned int message, + WPARAM w_param, + LPARAM l_param) { + // Don't track forwarded mouse messages. We expect the caller to track the + // mouse. + return HandleMouseEventInternal(message, w_param, l_param, false); +} + +LRESULT HWNDMessageHandler::HandleTouchMessage(unsigned int message, + WPARAM w_param, + LPARAM l_param) { + return OnTouchEvent(message, w_param, l_param); +} + +LRESULT HWNDMessageHandler::HandleKeyboardMessage(unsigned int message, + WPARAM w_param, + LPARAM l_param) { + return OnKeyEvent(message, w_param, l_param); +} -base::EventStatus HWNDMessageHandler::WillProcessEvent( - const base::NativeEvent& event) { - return base::EVENT_CONTINUE; +LRESULT HWNDMessageHandler::HandleScrollMessage(unsigned int message, + WPARAM w_param, + LPARAM l_param) { + return OnScrollMessage(message, w_param, l_param); } -void HWNDMessageHandler::DidProcessEvent(const base::NativeEvent& event) { - RedrawInvalidRect(); +LRESULT HWNDMessageHandler::HandleNcHitTestMessage(unsigned int message, + WPARAM w_param, + LPARAM l_param) { + return OnNCHitTest( + gfx::Point(CR_GET_X_LPARAM(l_param), CR_GET_Y_LPARAM(l_param))); } //////////////////////////////////////////////////////////////////////////////// @@ -1004,10 +971,12 @@ void HWNDMessageHandler::DidProcessEvent(const base::NativeEvent& event) { int HWNDMessageHandler::GetAppbarAutohideEdges(HMONITOR monitor) { autohide_factory_.InvalidateWeakPtrs(); - return Appbar::instance()->GetAutohideEdges( - monitor, - base::Bind(&HWNDMessageHandler::OnAppbarAutohideEdgesChanged, - autohide_factory_.GetWeakPtr())); + return ViewsDelegate::views_delegate ? + ViewsDelegate::views_delegate->GetAppbarAutohideEdges( + monitor, + base::Bind(&HWNDMessageHandler::OnAppbarAutohideEdgesChanged, + autohide_factory_.GetWeakPtr())) : + ViewsDelegate::EDGE_BOTTOM; } void HWNDMessageHandler::OnAppbarAutohideEdgesChanged() { @@ -1029,33 +998,10 @@ void HWNDMessageHandler::SetInitialFocus() { void HWNDMessageHandler::PostProcessActivateMessage(int activation_state, bool minimized) { - DCHECK(delegate_->CanSaveFocus()); - - bool active = activation_state != WA_INACTIVE && !minimized; + DCHECK(IsTopLevelWindow(hwnd())); + const bool active = activation_state != WA_INACTIVE && !minimized; if (delegate_->CanActivate()) delegate_->HandleActivationChanged(active); - - if (!active) { - // We might get activated/inactivated without being enabled, so we need to - // clear restore_focus_when_enabled_. - restore_focus_when_enabled_ = false; - delegate_->SaveFocusOnDeactivate(); - } else { - // We must restore the focus after the message has been DefProc'ed as it - // does set the focus to the last focused HWND. - // Note that if the window is not enabled, we cannot restore the focus as - // calling ::SetFocus on a child of the non-enabled top-window would fail. - // This is the case when showing a modal dialog (such as 'open file', - // 'print'...) from a different thread. - // In that case we delay the focus restoration to when the window is enabled - // again. - if (!IsWindowEnabled(hwnd())) { - DCHECK(!restore_focus_when_enabled_); - restore_focus_when_enabled_ = true; - return; - } - delegate_->RestoreFocusOnActivate(); - } } void HWNDMessageHandler::RestoreEnabledIfNecessary() { @@ -1153,7 +1099,7 @@ void HWNDMessageHandler::ResetWindowRegion(bool force, bool redraw) { HRGN current_rgn = CreateRectRgn(0, 0, 0, 0); int current_rgn_result = GetWindowRgn(hwnd(), current_rgn); - CRect window_rect; + RECT window_rect; GetWindowRect(hwnd(), &window_rect); HRGN new_region; if (custom_window_region_) { @@ -1163,14 +1109,15 @@ void HWNDMessageHandler::ResetWindowRegion(bool force, bool redraw) { HMONITOR monitor = MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONEAREST); MONITORINFO mi; mi.cbSize = sizeof mi; - base::win::GetMonitorInfoWrapper(monitor, &mi); - CRect work_rect = mi.rcWork; - work_rect.OffsetRect(-window_rect.left, -window_rect.top); + GetMonitorInfo(monitor, &mi); + RECT work_rect = mi.rcWork; + OffsetRect(&work_rect, -window_rect.left, -window_rect.top); new_region = CreateRectRgnIndirect(&work_rect); } else { gfx::Path window_mask; - delegate_->GetWindowMask( - gfx::Size(window_rect.Width(), window_rect.Height()), &window_mask); + delegate_->GetWindowMask(gfx::Size(window_rect.right - window_rect.left, + window_rect.bottom - window_rect.top), + &window_mask); new_region = gfx::CreateHRGNFromSkPath(window_mask); } @@ -1187,8 +1134,11 @@ void HWNDMessageHandler::ResetWindowRegion(bool force, bool redraw) { void HWNDMessageHandler::UpdateDwmNcRenderingPolicy() { if (base::win::GetVersion() < base::win::VERSION_VISTA) return; - DWMNCRENDERINGPOLICY policy = custom_window_region_ ? DWMNCRP_DISABLED - : DWMNCRP_USEWINDOWSTYLE; + + DWMNCRENDERINGPOLICY policy = + custom_window_region_ || delegate_->IsUsingCustomFrame() ? + DWMNCRP_DISABLED : DWMNCRP_ENABLED; + DwmSetWindowAttribute(hwnd(), DWMWA_NCRENDERING_POLICY, &policy, sizeof(DWMNCRENDERINGPOLICY)); } @@ -1206,15 +1156,6 @@ LRESULT HWNDMessageHandler::DefWindowProcWithRedrawLock(UINT message, return result; } -void HWNDMessageHandler::NotifyOwnedWindowsParentClosing() { - FindOwnedWindowsData data; - data.window = hwnd(); - EnumThreadWindows(GetCurrentThreadId(), FindOwnedWindowsCallback, - reinterpret_cast<LPARAM>(&data)); - for (size_t i = 0; i < data.owned_widgets.size(); ++i) - data.owned_widgets[i]->OnOwnerClosing(); -} - void HWNDMessageHandler::LockUpdates(bool force) { // We skip locked updates when Aero is on for two reasons: // 1. Because it isn't necessary @@ -1236,28 +1177,13 @@ void HWNDMessageHandler::UnlockUpdates(bool force) { } } -void HWNDMessageHandler::RedrawInvalidRect() { -// TODO(cpu): Remove the caller and this class as a message loop observer -// because we don't need agressive repaints via RDW_UPDATENOW in Aura. The -// general tracking bug for repaint issues is 177115. -#if !defined(USE_AURA) - if (!use_layered_buffer_) { - RECT r = { 0, 0, 0, 0 }; - if (GetUpdateRect(hwnd(), &r, FALSE) && !IsRectEmpty(&r)) { - RedrawWindow(hwnd(), &r, NULL, - RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOCHILDREN); - } - } -#endif -} - void HWNDMessageHandler::RedrawLayeredWindowContents() { waiting_for_redraw_layered_window_contents_ = false; if (invalid_rect_.IsEmpty()) return; // We need to clip to the dirty rect ourselves. - layered_window_contents_->sk_canvas()->save(SkCanvas::kClip_SaveFlag); + layered_window_contents_->sk_canvas()->save(); double scale = gfx::win::GetDeviceScaleFactor(); layered_window_contents_->sk_canvas()->scale( SkScalar(scale),SkScalar(scale)); @@ -1347,7 +1273,6 @@ void HWNDMessageHandler::OnCommand(UINT notification_code, LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) { use_layered_buffer_ = !!(window_ex_style() & WS_EX_LAYERED); -#if defined(USE_AURA) if (window_ex_style() & WS_EX_COMPOSITED) { if (base::win::GetVersion() >= base::win::VERSION_VISTA) { // This is part of the magic to emulate layered windows with Aura @@ -1356,7 +1281,6 @@ LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) { DwmExtendFrameIntoClientArea(hwnd(), &margins); } } -#endif fullscreen_handler_->set_hwnd(hwnd()); @@ -1385,14 +1309,6 @@ LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) { // creation time. ClientAreaSizeChanged(); -#if !defined(USE_AURA) - // We need to add ourselves as a message loop observer so that we can repaint - // aggressively if the contents of our window become invalid. Unfortunately - // WM_PAINT messages are starved and we get flickery redrawing when resizing - // if we do not do this. - base::MessageLoopForUI::current()->AddObserver(this); -#endif - delegate_->HandleCreate(); WTSRegisterSessionNotification(hwnd(), NOTIFY_FOR_THIS_SESSION); @@ -1407,7 +1323,7 @@ void HWNDMessageHandler::OnDestroy() { } void HWNDMessageHandler::OnDisplayChange(UINT bits_per_pixel, - const CSize& screen_size) { + const gfx::Size& screen_size) { delegate_->HandleDisplayChange(); } @@ -1423,6 +1339,11 @@ LRESULT HWNDMessageHandler::OnDwmCompositionChanged(UINT msg, return 0; } +void HWNDMessageHandler::OnEnterMenuLoop(BOOL from_track_popup_menu) { + if (menu_depth_++ == 0) + delegate_->HandleMenuLoop(true); +} + void HWNDMessageHandler::OnEnterSizeMove() { // Please refer to the comments in the OnSize function about the scrollbar // hack. @@ -1440,6 +1361,12 @@ LRESULT HWNDMessageHandler::OnEraseBkgnd(HDC dc) { return 1; } +void HWNDMessageHandler::OnExitMenuLoop(BOOL is_shortcut_menu) { + if (--menu_depth_ == 0) + delegate_->HandleMenuLoop(false); + DCHECK_GE(0, menu_depth_); +} + void HWNDMessageHandler::OnExitSizeMove() { delegate_->HandleEndWMSizeMove(); SetMsgHandled(FALSE); @@ -1460,13 +1387,16 @@ void HWNDMessageHandler::OnGetMinMaxInfo(MINMAXINFO* minmax_info) { // Add the native frame border size to the minimum and maximum size if the // view reports its size as the client size. if (delegate_->WidgetSizeIsClientSize()) { - CRect client_rect, window_rect; + RECT client_rect, window_rect; GetClientRect(hwnd(), &client_rect); GetWindowRect(hwnd(), &window_rect); - window_rect -= client_rect; - min_window_size.Enlarge(window_rect.Width(), window_rect.Height()); - if (!max_window_size.IsEmpty()) - max_window_size.Enlarge(window_rect.Width(), window_rect.Height()); + CR_DEFLATE_RECT(&window_rect, &client_rect); + min_window_size.Enlarge(window_rect.right - window_rect.left, + window_rect.bottom - window_rect.top); + if (!max_window_size.IsEmpty()) { + max_window_size.Enlarge(window_rect.right - window_rect.left, + window_rect.bottom - window_rect.top); + } } minmax_info->ptMinTrackSize.x = min_window_size.width(); minmax_info->ptMinTrackSize.y = min_window_size.height(); @@ -1486,8 +1416,12 @@ LRESULT HWNDMessageHandler::OnGetObject(UINT message, LPARAM l_param) { LRESULT reference_result = static_cast<LRESULT>(0L); + // Only the lower 32 bits of l_param are valid when checking the object id + // because it sometimes gets sign-extended incorrectly (but not always). + DWORD obj_id = static_cast<DWORD>(static_cast<DWORD_PTR>(l_param)); + // Accessibility readers will send an OBJID_CLIENT message - if (OBJID_CLIENT == l_param) { + if (OBJID_CLIENT == obj_id) { // Retrieve MSAA dispatch object for the root view. base::win::ScopedComPtr<IAccessible> root( delegate_->GetNativeViewAccessible()); @@ -1516,13 +1450,21 @@ void HWNDMessageHandler::OnInitMenu(HMENU menu) { bool is_restored = !is_fullscreen && !is_minimized && !is_maximized; ScopedRedrawLock lock(this); - EnableMenuItemByCommand(menu, SC_RESTORE, is_minimized || is_maximized); + EnableMenuItemByCommand(menu, SC_RESTORE, delegate_->CanResize() && + (is_minimized || is_maximized)); EnableMenuItemByCommand(menu, SC_MOVE, is_restored); EnableMenuItemByCommand(menu, SC_SIZE, delegate_->CanResize() && is_restored); EnableMenuItemByCommand(menu, SC_MAXIMIZE, delegate_->CanMaximize() && !is_fullscreen && !is_maximized); + // TODO: unfortunately, WidgetDelegate does not declare CanMinimize() and some + // code depends on this check, see http://crbug.com/341010. EnableMenuItemByCommand(menu, SC_MINIMIZE, delegate_->CanMaximize() && !is_minimized); + + if (is_maximized && delegate_->CanResize()) + ::SetMenuDefaultItem(menu, SC_RESTORE, FALSE); + else if (!is_maximized && delegate_->CanMaximize()) + ::SetMenuDefaultItem(menu, SC_MAXIMIZE, FALSE); } void HWNDMessageHandler::OnInputLangChange(DWORD character_set, @@ -1548,7 +1490,11 @@ void HWNDMessageHandler::OnKillFocus(HWND focused_window) { LRESULT HWNDMessageHandler::OnMouseActivate(UINT message, WPARAM w_param, LPARAM l_param) { -#if defined(USE_AURA) + // Please refer to the comments in the header for the touch_down_contexts_ + // member for the if statement below. + if (touch_down_contexts_) + return MA_NOACTIVATE; + // On Windows, if we select the menu item by touch and if the window at the // location is another window on the same thread, that window gets a // WM_MOUSEACTIVATE message and ends up activating itself, which is not @@ -1556,8 +1502,8 @@ LRESULT HWNDMessageHandler::OnMouseActivate(UINT message, // current cursor location. We check for this property in our // WM_MOUSEACTIVATE handler and don't activate the window if the property is // set. - if (::GetProp(hwnd(), kIgnoreTouchMouseActivateForWindow)) { - ::RemoveProp(hwnd(), kIgnoreTouchMouseActivateForWindow); + if (::GetProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow)) { + ::RemoveProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow); return MA_NOACTIVATE; } // A child window activation should be treated as if we lost activation. @@ -1567,7 +1513,7 @@ LRESULT HWNDMessageHandler::OnMouseActivate(UINT message, HWND child = ::RealChildWindowFromPoint(hwnd(), cursor_pos); if (::IsWindow(child) && child != hwnd() && ::IsWindowVisible(child)) PostProcessActivateMessage(WA_INACTIVE, false); -#endif + // TODO(beng): resolve this with the GetWindowLong() check on the subsequent // line. if (delegate_->IsWidgetWindow()) @@ -1581,120 +1527,10 @@ LRESULT HWNDMessageHandler::OnMouseActivate(UINT message, LRESULT HWNDMessageHandler::OnMouseRange(UINT message, WPARAM w_param, LPARAM l_param) { -#if defined(USE_AURA) - if (!touch_ids_.empty()) - return 0; - // We handle touch events on Windows Aura. Windows generates synthesized - // mouse messages in response to touch which we should ignore. However touch - // messages are only received for the client area. We need to ignore the - // synthesized mouse messages for all points in the client area and places - // which return HTNOWHERE. - if (ui::IsMouseEventFromTouch(message)) { - LPARAM l_param_ht = l_param; - // For mouse events (except wheel events), location is in window coordinates - // and should be converted to screen coordinates for WM_NCHITTEST. - if (message != WM_MOUSEWHEEL && message != WM_MOUSEHWHEEL) { - CPoint screen_point(l_param_ht); - MapWindowPoints(hwnd(), HWND_DESKTOP, &screen_point, 1); - l_param_ht = MAKELPARAM(screen_point.x, screen_point.y); - } - LRESULT hittest = SendMessage(hwnd(), WM_NCHITTEST, 0, l_param_ht); - if (hittest == HTCLIENT || hittest == HTNOWHERE) - return 0; - } -#endif - if (message == WM_RBUTTONUP && is_right_mouse_pressed_on_caption_) { - is_right_mouse_pressed_on_caption_ = false; - ReleaseCapture(); - // |point| is in window coordinates, but WM_NCHITTEST and TrackPopupMenu() - // expect screen coordinates. - CPoint screen_point(l_param); - MapWindowPoints(hwnd(), HWND_DESKTOP, &screen_point, 1); - w_param = SendMessage(hwnd(), WM_NCHITTEST, 0, - MAKELPARAM(screen_point.x, screen_point.y)); - if (w_param == HTCAPTION || w_param == HTSYSMENU) { - gfx::ShowSystemMenuAtPoint(hwnd(), gfx::Point(screen_point)); - return 0; - } - } else if (message == WM_NCLBUTTONDOWN && delegate_->IsUsingCustomFrame()) { - switch (w_param) { - case HTCLOSE: - case HTMINBUTTON: - case HTMAXBUTTON: { - // When the mouse is pressed down in these specific non-client areas, - // we need to tell the RootView to send the mouse pressed event (which - // sets capture, allowing subsequent WM_LBUTTONUP (note, _not_ - // WM_NCLBUTTONUP) to fire so that the appropriate WM_SYSCOMMAND can be - // sent by the applicable button's ButtonListener. We _have_ to do this - // way rather than letting Windows just send the syscommand itself (as - // would happen if we never did this dance) because for some insane - // reason DefWindowProc for WM_NCLBUTTONDOWN also renders the pressed - // window control button appearance, in the Windows classic style, over - // our view! Ick! By handling this message we prevent Windows from - // doing this undesirable thing, but that means we need to roll the - // sys-command handling ourselves. - // Combine |w_param| with common key state message flags. - w_param |= base::win::IsCtrlPressed() ? MK_CONTROL : 0; - w_param |= base::win::IsShiftPressed() ? MK_SHIFT : 0; - } - } - } else if (message == WM_NCRBUTTONDOWN && - (w_param == HTCAPTION || w_param == HTSYSMENU)) { - is_right_mouse_pressed_on_caption_ = true; - // We SetCapture() to ensure we only show the menu when the button - // down and up are both on the caption. Note: this causes the button up to - // be WM_RBUTTONUP instead of WM_NCRBUTTONUP. - SetCapture(); - } - - MSG msg = { hwnd(), message, w_param, l_param, GetMessageTime(), - { GET_X_LPARAM(l_param), GET_Y_LPARAM(l_param) } }; - ui::MouseEvent event(msg); - if (!touch_ids_.empty() || ui::IsMouseEventFromTouch(message)) - event.set_flags(event.flags() | ui::EF_FROM_TOUCH); - - if (!(event.flags() & ui::EF_IS_NON_CLIENT)) - delegate_->HandleTooltipMouseMove(message, w_param, l_param); - - if (event.type() == ui::ET_MOUSE_MOVED && !HasCapture()) { - // Windows only fires WM_MOUSELEAVE events if the application begins - // "tracking" mouse events for a given HWND during WM_MOUSEMOVE events. - // We need to call |TrackMouseEvents| to listen for WM_MOUSELEAVE. - TrackMouseEvents((message == WM_NCMOUSEMOVE) ? - TME_NONCLIENT | TME_LEAVE : TME_LEAVE); - } else if (event.type() == ui::ET_MOUSE_EXITED) { - // Reset our tracking flags so future mouse movement over this - // NativeWidgetWin results in a new tracking session. Fall through for - // OnMouseEvent. - active_mouse_tracking_flags_ = 0; - } else if (event.type() == ui::ET_MOUSEWHEEL) { - // Reroute the mouse wheel to the window under the pointer if applicable. - return (ui::RerouteMouseWheel(hwnd(), w_param, l_param) || - delegate_->HandleMouseEvent(ui::MouseWheelEvent(msg))) ? 0 : 1; - } - - // There are cases where the code handling the message destroys the window, - // so use the weak ptr to check if destruction occured or not. - base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr()); - bool handled = delegate_->HandleMouseEvent(event); - if (!ref.get()) - return 0; - if (!handled && message == WM_NCLBUTTONDOWN && w_param != HTSYSMENU && - delegate_->IsUsingCustomFrame()) { - // TODO(msw): Eliminate undesired painting, or re-evaluate this workaround. - // DefWindowProc for WM_NCLBUTTONDOWN does weird non-client painting, so we - // need to call it inside a ScopedRedrawLock. This may cause other negative - // side-effects (ex/ stifling non-client mouse releases). - DefWindowProcWithRedrawLock(message, w_param, l_param); - handled = true; - } - - if (ref.get()) - SetMsgHandled(handled); - return 0; + return HandleMouseEventInternal(message, w_param, l_param, true); } -void HWNDMessageHandler::OnMove(const CPoint& point) { +void HWNDMessageHandler::OnMove(const gfx::Point& point) { delegate_->HandleMove(); SetMsgHandled(FALSE); } @@ -1814,9 +1650,9 @@ LRESULT HWNDMessageHandler::OnNCCalcSize(BOOL mode, LPARAM l_param) { } } const int autohide_edges = GetAppbarAutohideEdges(monitor); - if (autohide_edges & Appbar::EDGE_LEFT) + if (autohide_edges & ViewsDelegate::EDGE_LEFT) client_rect->left += kAutoHideTaskbarThicknessPx; - if (autohide_edges & Appbar::EDGE_TOP) { + if (autohide_edges & ViewsDelegate::EDGE_TOP) { if (!delegate_->IsUsingCustomFrame()) { // Tricky bit. Due to a bug in DwmDefWindowProc()'s handling of // WM_NCHITTEST, having any nonclient area atop the window causes the @@ -1832,9 +1668,9 @@ LRESULT HWNDMessageHandler::OnNCCalcSize(BOOL mode, LPARAM l_param) { client_rect->top += kAutoHideTaskbarThicknessPx; } } - if (autohide_edges & Appbar::EDGE_RIGHT) + if (autohide_edges & ViewsDelegate::EDGE_RIGHT) client_rect->right -= kAutoHideTaskbarThicknessPx; - if (autohide_edges & Appbar::EDGE_BOTTOM) + if (autohide_edges & ViewsDelegate::EDGE_BOTTOM) client_rect->bottom -= kAutoHideTaskbarThicknessPx; // We cannot return WVR_REDRAW when there is nonclient area, or Windows @@ -1855,7 +1691,7 @@ LRESULT HWNDMessageHandler::OnNCCalcSize(BOOL mode, LPARAM l_param) { return mode ? WVR_REDRAW : 0; } -LRESULT HWNDMessageHandler::OnNCHitTest(const CPoint& point) { +LRESULT HWNDMessageHandler::OnNCHitTest(const gfx::Point& point) { if (!delegate_->IsWidgetWindow()) { SetMsgHandled(FALSE); return 0; @@ -1866,14 +1702,14 @@ LRESULT HWNDMessageHandler::OnNCHitTest(const CPoint& point) { if (!remove_standard_frame_ && !delegate_->IsUsingCustomFrame()) { LRESULT result; if (DwmDefWindowProc(hwnd(), WM_NCHITTEST, 0, - MAKELPARAM(point.x, point.y), &result)) { + MAKELPARAM(point.x(), point.y()), &result)) { return result; } } // First, give the NonClientView a chance to test the point to see if it // provides any of the non-client area. - POINT temp = point; + POINT temp = { point.x(), point.y() }; MapWindowPoints(HWND_DESKTOP, hwnd(), &temp, 1); int component = delegate_->GetNonClientComponent(gfx::Point(temp)); if (component != HTNOWHERE) @@ -1881,9 +1717,8 @@ LRESULT HWNDMessageHandler::OnNCHitTest(const CPoint& point) { // Otherwise, we let Windows do all the native frame non-client handling for // us. -#if defined(USE_AURA) LRESULT hit_test_code = DefWindowProc(hwnd(), WM_NCHITTEST, 0, - MAKELPARAM(point.x, point.y)); + MAKELPARAM(point.x(), point.y())); if (needs_scroll_styles_) { switch (hit_test_code) { // If we faked the WS_VSCROLL and WS_HSCROLL styles for this window, then @@ -1916,8 +1751,8 @@ LRESULT HWNDMessageHandler::OnNCHitTest(const CPoint& point) { window_rect.left = window_rect.right - scroll_width; window_rect.top = window_rect.bottom - scroll_height; POINT pt; - pt.x = point.x; - pt.y = point.y; + pt.x = point.x(); + pt.y = point.y(); if (::PtInRect(&window_rect, pt)) hit_test_code = HTCLIENT; break; @@ -1928,9 +1763,6 @@ LRESULT HWNDMessageHandler::OnNCHitTest(const CPoint& point) { } } return hit_test_code; -#else - SetMsgHandled(FALSE); -#endif } void HWNDMessageHandler::OnNCPaint(HRGN rgn) { @@ -1945,11 +1777,12 @@ void HWNDMessageHandler::OnNCPaint(HRGN rgn) { // We have an NC region and need to paint it. We expand the NC region to // include the dirty region of the root view. This is done to minimize // paints. - CRect window_rect; + RECT window_rect; GetWindowRect(hwnd(), &window_rect); gfx::Size root_view_size = delegate_->GetRootViewSize(); - if (gfx::Size(window_rect.Width(), window_rect.Height()) != root_view_size) { + if (gfx::Size(window_rect.right - window_rect.left, + window_rect.bottom - window_rect.top) != root_view_size) { // If the size of the window differs from the size of the root view it // means we're being asked to paint before we've gotten a WM_SIZE. This can // happen when the user is interactively resizing the window. To avoid @@ -1959,10 +1792,13 @@ void HWNDMessageHandler::OnNCPaint(HRGN rgn) { return; } - CRect dirty_region; + RECT dirty_region; // A value of 1 indicates paint all. if (!rgn || rgn == reinterpret_cast<HRGN>(1)) { - dirty_region = CRect(0, 0, window_rect.Width(), window_rect.Height()); + dirty_region.left = 0; + dirty_region.top = 0; + dirty_region.right = window_rect.right - window_rect.left; + dirty_region.bottom = window_rect.bottom - window_rect.top; } else { RECT rgn_bounding_box; GetRgnBox(rgn, &rgn_bounding_box); @@ -1991,8 +1827,8 @@ void HWNDMessageHandler::OnNCPaint(HRGN rgn) { // The root view has a region that needs to be painted. Include it in the // region we're going to paint. - CRect old_paint_region_crect = old_paint_region.ToRECT(); - CRect tmp = dirty_region; + RECT old_paint_region_crect = old_paint_region.ToRECT(); + RECT tmp = dirty_region; UnionRect(&dirty_region, &tmp, &old_paint_region_crect); } @@ -2002,9 +1838,12 @@ void HWNDMessageHandler::OnNCPaint(HRGN rgn) { // the following in a block to force paint to occur so that we can release // the dc. if (!delegate_->HandlePaintAccelerated(gfx::Rect(dirty_region))) { - gfx::CanvasSkiaPaint canvas(dc, true, dirty_region.left, - dirty_region.top, dirty_region.Width(), - dirty_region.Height()); + gfx::CanvasSkiaPaint canvas(dc, + true, + dirty_region.left, + dirty_region.top, + dirty_region.right - dirty_region.left, + dirty_region.bottom - dirty_region.top); delegate_->HandlePaint(&canvas); } @@ -2050,13 +1889,7 @@ void HWNDMessageHandler::OnPaint(HDC dc) { // Try to paint accelerated first. if (!IsRectEmpty(&ps.rcPaint) && !delegate_->HandlePaintAccelerated(gfx::Rect(ps.rcPaint))) { -#if defined(USE_AURA) delegate_->HandlePaint(NULL); -#else - scoped_ptr<gfx::CanvasSkiaPaint> canvas( - new gfx::CanvasSkiaPaint(hwnd(), display_dc, ps)); - delegate_->HandlePaint(canvas.get()); -#endif } EndPaint(hwnd(), &ps); @@ -2120,6 +1953,9 @@ LRESULT HWNDMessageHandler::OnSetCursor(UINT message, case HTCLIENT: SetCursor(current_cursor_); return 1; + case LOWORD(HTERROR): // Use HTERROR's LOWORD value for valid comparison. + SetMsgHandled(FALSE); + break; default: // Use the default value, IDC_ARROW. break; @@ -2160,13 +1996,12 @@ void HWNDMessageHandler::OnSettingChange(UINT flags, const wchar_t* section) { } } -void HWNDMessageHandler::OnSize(UINT param, const CSize& size) { +void HWNDMessageHandler::OnSize(UINT param, const gfx::Size& size) { RedrawWindow(hwnd(), NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN); // ResetWindowRegion is going to trigger WM_NCPAINT. By doing it after we've // invoked OnSize we ensure the RootView has been laid out. ResetWindowRegion(false, true); -#if defined(USE_AURA) // We add the WS_VSCROLL and WS_HSCROLL styles to top level windows to ensure // that legacy trackpad/trackpoint drivers generate the WM_VSCROLL and // WM_HSCROLL messages and scrolling works. @@ -2179,11 +2014,10 @@ void HWNDMessageHandler::OnSize(UINT param, const CSize& size) { base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&AddScrollStylesToWindow, hwnd())); } -#endif } void HWNDMessageHandler::OnSysCommand(UINT notification_code, - const CPoint& point) { + const gfx::Point& point) { if (!delegate_->ShouldHandleSystemCommands()) return; @@ -2214,7 +2048,7 @@ void HWNDMessageHandler::OnSysCommand(UINT notification_code, // Handle SC_KEYMENU, which means that the user has pressed the ALT // key and released it, so we should focus the menu bar. - if ((notification_code & sc_mask) == SC_KEYMENU && point.x == 0) { + if ((notification_code & sc_mask) == SC_KEYMENU && point.x() == 0) { int modifiers = ui::EF_NONE; if (base::win::IsShiftPressed()) modifiers |= ui::EF_SHIFT_DOWN; @@ -2234,8 +2068,11 @@ void HWNDMessageHandler::OnSysCommand(UINT notification_code, // with the mouse/touch/keyboard, we flag as being in a size loop. if ((notification_code & sc_mask) == SC_SIZE) in_size_loop_ = true; + base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr()); DefWindowProc(hwnd(), WM_SYSCOMMAND, notification_code, - MAKELPARAM(point.x, point.y)); + MAKELPARAM(point.x(), point.y())); + if (!ref.get()) + return; in_size_loop_ = false; } } @@ -2248,22 +2085,44 @@ LRESULT HWNDMessageHandler::OnTouchEvent(UINT message, WPARAM w_param, LPARAM l_param) { // Handle touch events only on Aura for now. -#if !defined(USE_AURA) - SetMsgHandled(FALSE); - return 0; -#endif int num_points = LOWORD(w_param); scoped_ptr<TOUCHINPUT[]> input(new TOUCHINPUT[num_points]); if (ui::GetTouchInputInfoWrapper(reinterpret_cast<HTOUCHINPUT>(l_param), num_points, input.get(), sizeof(TOUCHINPUT))) { + int flags = ui::GetModifiersFromKeyState(); TouchEvents touch_events; for (int i = 0; i < num_points; ++i) { + POINT point; + point.x = TOUCH_COORD_TO_PIXEL(input[i].x); + point.y = TOUCH_COORD_TO_PIXEL(input[i].y); + + if (base::win::GetVersion() == base::win::VERSION_WIN7) { + // Windows 7 sends touch events for touches in the non-client area, + // whereas Windows 8 does not. In order to unify the behaviour, always + // ignore touch events in the non-client area. + LPARAM l_param_ht = MAKELPARAM(point.x, point.y); + LRESULT hittest = SendMessage(hwnd(), WM_NCHITTEST, 0, l_param_ht); + + if (hittest != HTCLIENT) + return 0; + } + + ScreenToClient(hwnd(), &point); + + last_touch_message_time_ = ::GetMessageTime(); + ui::EventType touch_event_type = ui::ET_UNKNOWN; if (input[i].dwFlags & TOUCHEVENTF_DOWN) { touch_ids_.insert(input[i].dwID); touch_event_type = ui::ET_TOUCH_PRESSED; + touch_down_contexts_++; + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&HWNDMessageHandler::ResetTouchDownContext, + weak_factory_.GetWeakPtr()), + base::TimeDelta::FromMilliseconds(kTouchDownContextResetTimeout)); } else if (input[i].dwFlags & TOUCHEVENTF_UP) { touch_ids_.erase(input[i].dwID); touch_event_type = ui::ET_TOUCH_RELEASED; @@ -2271,19 +2130,27 @@ LRESULT HWNDMessageHandler::OnTouchEvent(UINT message, touch_event_type = ui::ET_TOUCH_MOVED; } if (touch_event_type != ui::ET_UNKNOWN) { - POINT point; - point.x = TOUCH_COORD_TO_PIXEL(input[i].x) / - gfx::win::GetUndocumentedDPITouchScale(); - point.y = TOUCH_COORD_TO_PIXEL(input[i].y) / - gfx::win::GetUndocumentedDPITouchScale(); - - ScreenToClient(hwnd(), &point); - - ui::TouchEvent event( - touch_event_type, - gfx::Point(point.x, point.y), - id_generator_.GetGeneratedID(input[i].dwID), - base::TimeDelta::FromMilliseconds(input[i].dwTime)); + base::TimeTicks now; + // input[i].dwTime doesn't necessarily relate to the system time at all, + // so use base::TimeTicks::HighResNow() if possible, or + // base::TimeTicks::Now() otherwise. + if (base::TimeTicks::IsHighResNowFastAndReliable()) + now = base::TimeTicks::HighResNow(); + else + now = base::TimeTicks::Now(); + ui::TouchEvent event(touch_event_type, + gfx::Point(point.x, point.y), + id_generator_.GetGeneratedID(input[i].dwID), + now - base::TimeTicks()); + event.set_flags(flags); + event.latency()->AddLatencyNumberWithTimestamp( + ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, + 0, + 0, + base::TimeTicks::FromInternalValue( + event.time_stamp().ToInternalValue()), + 1); + touch_events.push_back(event); if (touch_event_type == ui::ET_TOUCH_RELEASED) id_generator_.ReleaseNumber(input[i].dwID); @@ -2314,7 +2181,7 @@ void HWNDMessageHandler::OnWindowPosChanging(WINDOWPOS* window_pos) { window_pos->flags &= ~(SWP_SHOWWINDOW | SWP_HIDEWINDOW); } } else if (!GetParent(hwnd())) { - CRect window_rect; + RECT window_rect; HMONITOR monitor; gfx::Rect monitor_rect, work_area; if (GetWindowRect(hwnd(), &window_rect) && @@ -2368,6 +2235,9 @@ void HWNDMessageHandler::OnWindowPosChanging(WINDOWPOS* window_pos) { } } + if (DidClientAreaSizeChange(window_pos)) + delegate_->HandleWindowSizeChanging(); + if (ScopedFullscreenVisibility::IsHiddenForFullscreen(hwnd())) { // Prevent the window from being made visible if we've been asked to do so. // See comment in header as to why we might want this. @@ -2404,4 +2274,158 @@ void HWNDMessageHandler::HandleTouchEvents(const TouchEvents& touch_events) { delegate_->HandleTouchEvent(touch_events[i]); } +void HWNDMessageHandler::ResetTouchDownContext() { + touch_down_contexts_--; +} + +LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, + WPARAM w_param, + LPARAM l_param, + bool track_mouse) { + if (!touch_ids_.empty()) + return 0; + // We handle touch events on Windows Aura. Windows generates synthesized + // mouse messages in response to touch which we should ignore. However touch + // messages are only received for the client area. We need to ignore the + // synthesized mouse messages for all points in the client area and places + // which return HTNOWHERE. + if (ui::IsMouseEventFromTouch(message)) { + LPARAM l_param_ht = l_param; + // For mouse events (except wheel events), location is in window coordinates + // and should be converted to screen coordinates for WM_NCHITTEST. + if (message != WM_MOUSEWHEEL && message != WM_MOUSEHWHEEL) { + POINT screen_point = CR_POINT_INITIALIZER_FROM_LPARAM(l_param_ht); + MapWindowPoints(hwnd(), HWND_DESKTOP, &screen_point, 1); + l_param_ht = MAKELPARAM(screen_point.x, screen_point.y); + } + LRESULT hittest = SendMessage(hwnd(), WM_NCHITTEST, 0, l_param_ht); + if (hittest == HTCLIENT || hittest == HTNOWHERE) + return 0; + } + + // Certain logitech drivers send the WM_MOUSEHWHEEL message to the parent + // followed by WM_MOUSEWHEEL messages to the child window causing a vertical + // scroll. We treat these WM_MOUSEWHEEL messages as WM_MOUSEHWHEEL + // messages. + if (message == WM_MOUSEHWHEEL) + last_mouse_hwheel_time_ = ::GetMessageTime(); + + if (message == WM_MOUSEWHEEL && + ::GetMessageTime() == last_mouse_hwheel_time_) { + message = WM_MOUSEHWHEEL; + } + + if (message == WM_RBUTTONUP && is_right_mouse_pressed_on_caption_) { + is_right_mouse_pressed_on_caption_ = false; + ReleaseCapture(); + // |point| is in window coordinates, but WM_NCHITTEST and TrackPopupMenu() + // expect screen coordinates. + POINT screen_point = CR_POINT_INITIALIZER_FROM_LPARAM(l_param); + MapWindowPoints(hwnd(), HWND_DESKTOP, &screen_point, 1); + w_param = SendMessage(hwnd(), WM_NCHITTEST, 0, + MAKELPARAM(screen_point.x, screen_point.y)); + if (w_param == HTCAPTION || w_param == HTSYSMENU) { + gfx::ShowSystemMenuAtPoint(hwnd(), gfx::Point(screen_point)); + return 0; + } + } else if (message == WM_NCLBUTTONDOWN && delegate_->IsUsingCustomFrame()) { + switch (w_param) { + case HTCLOSE: + case HTMINBUTTON: + case HTMAXBUTTON: { + // When the mouse is pressed down in these specific non-client areas, + // we need to tell the RootView to send the mouse pressed event (which + // sets capture, allowing subsequent WM_LBUTTONUP (note, _not_ + // WM_NCLBUTTONUP) to fire so that the appropriate WM_SYSCOMMAND can be + // sent by the applicable button's ButtonListener. We _have_ to do this + // way rather than letting Windows just send the syscommand itself (as + // would happen if we never did this dance) because for some insane + // reason DefWindowProc for WM_NCLBUTTONDOWN also renders the pressed + // window control button appearance, in the Windows classic style, over + // our view! Ick! By handling this message we prevent Windows from + // doing this undesirable thing, but that means we need to roll the + // sys-command handling ourselves. + // Combine |w_param| with common key state message flags. + w_param |= base::win::IsCtrlPressed() ? MK_CONTROL : 0; + w_param |= base::win::IsShiftPressed() ? MK_SHIFT : 0; + } + } + } else if (message == WM_NCRBUTTONDOWN && + (w_param == HTCAPTION || w_param == HTSYSMENU)) { + is_right_mouse_pressed_on_caption_ = true; + // We SetCapture() to ensure we only show the menu when the button + // down and up are both on the caption. Note: this causes the button up to + // be WM_RBUTTONUP instead of WM_NCRBUTTONUP. + SetCapture(); + } + long message_time = GetMessageTime(); + MSG msg = { hwnd(), message, w_param, l_param, message_time, + { CR_GET_X_LPARAM(l_param), CR_GET_Y_LPARAM(l_param) } }; + ui::MouseEvent event(msg); + if (IsSynthesizedMouseMessage(message, message_time, l_param)) + event.set_flags(event.flags() | ui::EF_FROM_TOUCH); + + if (!(event.flags() & ui::EF_IS_NON_CLIENT)) + delegate_->HandleTooltipMouseMove(message, w_param, l_param); + + if (event.type() == ui::ET_MOUSE_MOVED && !HasCapture() && track_mouse) { + // Windows only fires WM_MOUSELEAVE events if the application begins + // "tracking" mouse events for a given HWND during WM_MOUSEMOVE events. + // We need to call |TrackMouseEvents| to listen for WM_MOUSELEAVE. + TrackMouseEvents((message == WM_NCMOUSEMOVE) ? + TME_NONCLIENT | TME_LEAVE : TME_LEAVE); + } else if (event.type() == ui::ET_MOUSE_EXITED) { + // Reset our tracking flags so future mouse movement over this + // NativeWidget results in a new tracking session. Fall through for + // OnMouseEvent. + active_mouse_tracking_flags_ = 0; + } else if (event.type() == ui::ET_MOUSEWHEEL) { + // Reroute the mouse wheel to the window under the pointer if applicable. + return (ui::RerouteMouseWheel(hwnd(), w_param, l_param) || + delegate_->HandleMouseEvent(ui::MouseWheelEvent(msg))) ? 0 : 1; + } + + // There are cases where the code handling the message destroys the window, + // so use the weak ptr to check if destruction occured or not. + base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr()); + bool handled = delegate_->HandleMouseEvent(event); + if (!ref.get()) + return 0; + if (!handled && message == WM_NCLBUTTONDOWN && w_param != HTSYSMENU && + delegate_->IsUsingCustomFrame()) { + // TODO(msw): Eliminate undesired painting, or re-evaluate this workaround. + // DefWindowProc for WM_NCLBUTTONDOWN does weird non-client painting, so we + // need to call it inside a ScopedRedrawLock. This may cause other negative + // side-effects (ex/ stifling non-client mouse releases). + DefWindowProcWithRedrawLock(message, w_param, l_param); + handled = true; + } + + if (ref.get()) + SetMsgHandled(handled); + return 0; +} + +bool HWNDMessageHandler::IsSynthesizedMouseMessage(unsigned int message, + int message_time, + LPARAM l_param) { + if (ui::IsMouseEventFromTouch(message)) + return true; + // Ignore mouse messages which occur at the same location as the current + // cursor position and within a time difference of 500 ms from the last + // touch message. + if (last_touch_message_time_ && message_time >= last_touch_message_time_ && + ((message_time - last_touch_message_time_) <= + kSynthesizedMouseTouchMessagesTimeDifference)) { + POINT mouse_location = CR_POINT_INITIALIZER_FROM_LPARAM(l_param); + ::ClientToScreen(hwnd(), &mouse_location); + POINT cursor_pos = {0}; + ::GetCursorPos(&cursor_pos); + if (memcmp(&cursor_pos, &mouse_location, sizeof(POINT))) + return false; + return true; + } + return false; +} + } // namespace views diff --git a/chromium/ui/views/win/hwnd_message_handler.h b/chromium/ui/views/win/hwnd_message_handler.h index 5aacd7819d8..fbc05813ca0 100644 --- a/chromium/ui/views/win/hwnd_message_handler.h +++ b/chromium/ui/views/win/hwnd_message_handler.h @@ -6,9 +6,6 @@ #define UI_VIEWS_WIN_HWND_MESSAGE_HANDLER_H_ #include <windows.h> -#include <atlbase.h> -#include <atlapp.h> -#include <atlmisc.h> #include <set> #include <vector> @@ -17,12 +14,12 @@ #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" -#include "base/message_loop/message_loop.h" #include "base/strings/string16.h" #include "base/win/scoped_gdi_object.h" #include "base/win/win_util.h" -#include "ui/base/accessibility/accessibility_types.h" +#include "ui/accessibility/ax_enums.h" #include "ui/base/ui_base_types.h" +#include "ui/base/win/window_event_target.h" #include "ui/events/event.h" #include "ui/gfx/rect.h" #include "ui/gfx/sequential_id_generator.h" @@ -36,6 +33,10 @@ class ImageSkia; class Insets; } +namespace ui { +class ViewProp; +} + namespace views { class FullscreenHandler; @@ -77,10 +78,12 @@ const int WM_NCUAHDRAWFRAME = 0xAF; LPARAM l_param, \ LRESULT& l_result, \ DWORD msg_map_id = 0) { \ + base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr()); \ BOOL old_msg_handled = msg_handled_; \ BOOL ret = _ProcessWindowMessage(hwnd, msg, w_param, l_param, l_result, \ msg_map_id); \ - msg_handled_ = old_msg_handled; \ + if (ref.get()) \ + msg_handled_ = old_msg_handled; \ return ret; \ } \ BOOL _ProcessWindowMessage(HWND hWnd, \ @@ -103,13 +106,13 @@ const int WM_NCUAHDRAWFRAME = 0xAF; // An object that handles messages for a HWND that implements the views // "Custom Frame" look. The purpose of this class is to isolate the windows- // specific message handling from the code that wraps it. It is intended to be -// used by both a views::NativeWidget and an aura::RootWindowHost +// used by both a views::NativeWidget and an aura::WindowTreeHost // implementation. // TODO(beng): This object should eventually *become* the WindowImpl. class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl, public internal::InputMethodDelegate, - public base::MessageLoopForUI::Observer { + public ui::WindowEventTarget { public: explicit HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate); ~HWNDMessageHandler(); @@ -129,7 +132,10 @@ class VIEWS_EXPORT HWNDMessageHandler : void GetWindowPlacement(gfx::Rect* bounds, ui::WindowShowState* show_state) const; - void SetBounds(const gfx::Rect& bounds_in_pixels); + // Sets the bounds of the HWND to |bounds_in_pixels|. If the HWND size is not + // changed, |force_size_changed| determines if we should pretend it is. + void SetBounds(const gfx::Rect& bounds_in_pixels, bool force_size_changed); + void SetSize(const gfx::Size& size); void CenterWindow(const gfx::Size& size); @@ -140,8 +146,6 @@ class VIEWS_EXPORT HWNDMessageHandler : void Show(); void ShowWindowWithState(ui::WindowShowState show_state); - // TODO(beng): distinguish from ShowWindowWithState(). - void Show(int show_state); void ShowMaximizedWithBounds(const gfx::Rect& bounds); void Hide(); @@ -179,7 +183,7 @@ class VIEWS_EXPORT HWNDMessageHandler : void SetVisibilityChangedAnimationsEnabled(bool enabled); // Returns true if the title changed. - bool SetTitle(const string16& title); + bool SetTitle(const base::string16& title); void SetCursor(HCURSOR cursor); @@ -211,16 +215,32 @@ class VIEWS_EXPORT HWNDMessageHandler : WPARAM w_param, LPARAM l_param) OVERRIDE; - // Overridden from MessageLoopForUI::Observer: - virtual base::EventStatus WillProcessEvent( - const base::NativeEvent& event) OVERRIDE; - virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE; + // Overridden from WindowEventTarget + virtual LRESULT HandleMouseMessage(unsigned int message, + WPARAM w_param, + LPARAM l_param) OVERRIDE; + virtual LRESULT HandleKeyboardMessage(unsigned int message, + WPARAM w_param, + LPARAM l_param) OVERRIDE; + virtual LRESULT HandleTouchMessage(unsigned int message, + WPARAM w_param, + LPARAM l_param) OVERRIDE; + + virtual LRESULT HandleScrollMessage(unsigned int message, + WPARAM w_param, + LPARAM l_param) OVERRIDE; + + virtual LRESULT HandleNcHitTestMessage(unsigned int message, + WPARAM w_param, + LPARAM l_param) OVERRIDE; - // Returns the auto-hide edges of the appbar. See Appbar::GetAutohideEdges() - // for details. If the edges change OnAppbarAutohideEdgesChanged() is called. + // Returns the auto-hide edges of the appbar. See + // ViewsDelegate::GetAppbarAutohideEdges() for details. If the edges change, + // OnAppbarAutohideEdgesChanged() is called. int GetAppbarAutohideEdges(HMONITOR monitor); - // Callback if the autohide edges have changed. See Appbar for details. + // Callback if the autohide edges have changed. See + // ViewsDelegate::GetAppbarAutohideEdges() for details. void OnAppbarAutohideEdgesChanged(); // Can be called after the delegate has had the opportunity to set focus and @@ -267,9 +287,6 @@ class VIEWS_EXPORT HWNDMessageHandler : WPARAM w_param, LPARAM l_param); - // Notifies any owned windows that we're closing. - void NotifyOwnedWindowsParentClosing(); - // Lock or unlock the window from being able to redraw itself in response to // updates to its invalid region. class ScopedRedrawLock; @@ -279,9 +296,6 @@ class VIEWS_EXPORT HWNDMessageHandler : // Stops ignoring SetWindowPos() requests (see below). void StopIgnoringPosChanges() { ignore_window_pos_changes_ = false; } - // Synchronously paints the invalid contents of the Widget. - void RedrawInvalidRect(); - // Synchronously updates the invalid contents of the Widget. Valid for // layered windows only. void RedrawLayeredWindowContents(); @@ -294,90 +308,92 @@ class VIEWS_EXPORT HWNDMessageHandler : BEGIN_SAFE_MSG_MAP_EX(HWNDMessageHandler) // Range handlers must go first! - MESSAGE_RANGE_HANDLER_EX(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseRange) - MESSAGE_RANGE_HANDLER_EX(WM_NCMOUSEMOVE, WM_NCXBUTTONDBLCLK, OnMouseRange) + CR_MESSAGE_RANGE_HANDLER_EX(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseRange) + CR_MESSAGE_RANGE_HANDLER_EX(WM_NCMOUSEMOVE, + WM_NCXBUTTONDBLCLK, + OnMouseRange) // CustomFrameWindow hacks - MESSAGE_HANDLER_EX(WM_NCUAHDRAWCAPTION, OnNCUAHDrawCaption) - MESSAGE_HANDLER_EX(WM_NCUAHDRAWFRAME, OnNCUAHDrawFrame) + CR_MESSAGE_HANDLER_EX(WM_NCUAHDRAWCAPTION, OnNCUAHDrawCaption) + CR_MESSAGE_HANDLER_EX(WM_NCUAHDRAWFRAME, OnNCUAHDrawFrame) // Vista and newer - MESSAGE_HANDLER_EX(WM_DWMCOMPOSITIONCHANGED, OnDwmCompositionChanged) + CR_MESSAGE_HANDLER_EX(WM_DWMCOMPOSITIONCHANGED, OnDwmCompositionChanged) // Non-atlcrack.h handlers - MESSAGE_HANDLER_EX(WM_GETOBJECT, OnGetObject) + CR_MESSAGE_HANDLER_EX(WM_GETOBJECT, OnGetObject) // Mouse events. - MESSAGE_HANDLER_EX(WM_MOUSEACTIVATE, OnMouseActivate) - MESSAGE_HANDLER_EX(WM_MOUSELEAVE, OnMouseRange) - MESSAGE_HANDLER_EX(WM_NCMOUSELEAVE, OnMouseRange) - MESSAGE_HANDLER_EX(WM_SETCURSOR, OnSetCursor); + CR_MESSAGE_HANDLER_EX(WM_MOUSEACTIVATE, OnMouseActivate) + CR_MESSAGE_HANDLER_EX(WM_MOUSELEAVE, OnMouseRange) + CR_MESSAGE_HANDLER_EX(WM_NCMOUSELEAVE, OnMouseRange) + CR_MESSAGE_HANDLER_EX(WM_SETCURSOR, OnSetCursor); // Key events. - MESSAGE_HANDLER_EX(WM_KEYDOWN, OnKeyEvent) - MESSAGE_HANDLER_EX(WM_KEYUP, OnKeyEvent) - MESSAGE_HANDLER_EX(WM_SYSKEYDOWN, OnKeyEvent) - MESSAGE_HANDLER_EX(WM_SYSKEYUP, OnKeyEvent) + CR_MESSAGE_HANDLER_EX(WM_KEYDOWN, OnKeyEvent) + CR_MESSAGE_HANDLER_EX(WM_KEYUP, OnKeyEvent) + CR_MESSAGE_HANDLER_EX(WM_SYSKEYDOWN, OnKeyEvent) + CR_MESSAGE_HANDLER_EX(WM_SYSKEYUP, OnKeyEvent) // IME Events. - MESSAGE_HANDLER_EX(WM_IME_SETCONTEXT, OnImeMessages) - MESSAGE_HANDLER_EX(WM_IME_STARTCOMPOSITION, OnImeMessages) - MESSAGE_HANDLER_EX(WM_IME_COMPOSITION, OnImeMessages) - MESSAGE_HANDLER_EX(WM_IME_ENDCOMPOSITION, OnImeMessages) - MESSAGE_HANDLER_EX(WM_IME_REQUEST, OnImeMessages) - MESSAGE_HANDLER_EX(WM_IME_NOTIFY, OnImeMessages) - MESSAGE_HANDLER_EX(WM_CHAR, OnImeMessages) - MESSAGE_HANDLER_EX(WM_SYSCHAR, OnImeMessages) - MESSAGE_HANDLER_EX(WM_DEADCHAR, OnImeMessages) - MESSAGE_HANDLER_EX(WM_SYSDEADCHAR, OnImeMessages) + CR_MESSAGE_HANDLER_EX(WM_IME_SETCONTEXT, OnImeMessages) + CR_MESSAGE_HANDLER_EX(WM_IME_STARTCOMPOSITION, OnImeMessages) + CR_MESSAGE_HANDLER_EX(WM_IME_COMPOSITION, OnImeMessages) + CR_MESSAGE_HANDLER_EX(WM_IME_ENDCOMPOSITION, OnImeMessages) + CR_MESSAGE_HANDLER_EX(WM_IME_REQUEST, OnImeMessages) + CR_MESSAGE_HANDLER_EX(WM_IME_NOTIFY, OnImeMessages) + CR_MESSAGE_HANDLER_EX(WM_CHAR, OnImeMessages) + CR_MESSAGE_HANDLER_EX(WM_SYSCHAR, OnImeMessages) // Scroll events - MESSAGE_HANDLER_EX(WM_VSCROLL, OnScrollMessage) - MESSAGE_HANDLER_EX(WM_HSCROLL, OnScrollMessage) + CR_MESSAGE_HANDLER_EX(WM_VSCROLL, OnScrollMessage) + CR_MESSAGE_HANDLER_EX(WM_HSCROLL, OnScrollMessage) // Touch Events. - MESSAGE_HANDLER_EX(WM_TOUCH, OnTouchEvent) + CR_MESSAGE_HANDLER_EX(WM_TOUCH, OnTouchEvent) // Uses the general handler macro since the specific handler macro // MSG_WM_NCACTIVATE would convert WPARAM type to BOOL type. The high // word of WPARAM could be set when the window is minimized or restored. - MESSAGE_HANDLER_EX(WM_NCACTIVATE, OnNCActivate) + CR_MESSAGE_HANDLER_EX(WM_NCACTIVATE, OnNCActivate) // This list is in _ALPHABETICAL_ order! OR I WILL HURT YOU. - MSG_WM_ACTIVATEAPP(OnActivateApp) - MSG_WM_APPCOMMAND(OnAppCommand) - MSG_WM_CANCELMODE(OnCancelMode) - MSG_WM_CAPTURECHANGED(OnCaptureChanged) - MSG_WM_CLOSE(OnClose) - MSG_WM_COMMAND(OnCommand) - MSG_WM_CREATE(OnCreate) - MSG_WM_DESTROY(OnDestroy) - MSG_WM_DISPLAYCHANGE(OnDisplayChange) - MSG_WM_ENTERSIZEMOVE(OnEnterSizeMove) - MSG_WM_ERASEBKGND(OnEraseBkgnd) - MSG_WM_EXITSIZEMOVE(OnExitSizeMove) - MSG_WM_GETMINMAXINFO(OnGetMinMaxInfo) - MSG_WM_INITMENU(OnInitMenu) - MSG_WM_INPUTLANGCHANGE(OnInputLangChange) - MSG_WM_KILLFOCUS(OnKillFocus) - MSG_WM_MOVE(OnMove) - MSG_WM_MOVING(OnMoving) - MSG_WM_NCCALCSIZE(OnNCCalcSize) - MSG_WM_NCHITTEST(OnNCHitTest) - MSG_WM_NCPAINT(OnNCPaint) - MSG_WM_NOTIFY(OnNotify) - MSG_WM_PAINT(OnPaint) - MSG_WM_SETFOCUS(OnSetFocus) - MSG_WM_SETICON(OnSetIcon) - MSG_WM_SETTEXT(OnSetText) - MSG_WM_SETTINGCHANGE(OnSettingChange) - MSG_WM_SIZE(OnSize) - MSG_WM_SYSCOMMAND(OnSysCommand) - MSG_WM_THEMECHANGED(OnThemeChanged) - MSG_WM_WINDOWPOSCHANGED(OnWindowPosChanged) - MSG_WM_WINDOWPOSCHANGING(OnWindowPosChanging) - MSG_WM_WTSSESSION_CHANGE(OnSessionChange) - END_MSG_MAP() + CR_MSG_WM_ACTIVATEAPP(OnActivateApp) + CR_MSG_WM_APPCOMMAND(OnAppCommand) + CR_MSG_WM_CANCELMODE(OnCancelMode) + CR_MSG_WM_CAPTURECHANGED(OnCaptureChanged) + CR_MSG_WM_CLOSE(OnClose) + CR_MSG_WM_COMMAND(OnCommand) + CR_MSG_WM_CREATE(OnCreate) + CR_MSG_WM_DESTROY(OnDestroy) + CR_MSG_WM_DISPLAYCHANGE(OnDisplayChange) + CR_MSG_WM_ENTERMENULOOP(OnEnterMenuLoop) + CR_MSG_WM_EXITMENULOOP(OnExitMenuLoop) + CR_MSG_WM_ENTERSIZEMOVE(OnEnterSizeMove) + CR_MSG_WM_ERASEBKGND(OnEraseBkgnd) + CR_MSG_WM_EXITSIZEMOVE(OnExitSizeMove) + CR_MSG_WM_GETMINMAXINFO(OnGetMinMaxInfo) + CR_MSG_WM_INITMENU(OnInitMenu) + CR_MSG_WM_INPUTLANGCHANGE(OnInputLangChange) + CR_MSG_WM_KILLFOCUS(OnKillFocus) + CR_MSG_WM_MOVE(OnMove) + CR_MSG_WM_MOVING(OnMoving) + CR_MSG_WM_NCCALCSIZE(OnNCCalcSize) + CR_MSG_WM_NCHITTEST(OnNCHitTest) + CR_MSG_WM_NCPAINT(OnNCPaint) + CR_MSG_WM_NOTIFY(OnNotify) + CR_MSG_WM_PAINT(OnPaint) + CR_MSG_WM_SETFOCUS(OnSetFocus) + CR_MSG_WM_SETICON(OnSetIcon) + CR_MSG_WM_SETTEXT(OnSetText) + CR_MSG_WM_SETTINGCHANGE(OnSettingChange) + CR_MSG_WM_SIZE(OnSize) + CR_MSG_WM_SYSCOMMAND(OnSysCommand) + CR_MSG_WM_THEMECHANGED(OnThemeChanged) + CR_MSG_WM_WINDOWPOSCHANGED(OnWindowPosChanged) + CR_MSG_WM_WINDOWPOSCHANGING(OnWindowPosChanging) + CR_MSG_WM_WTSSESSION_CHANGE(OnSessionChange) + CR_END_MSG_MAP() // Message Handlers. // This list is in _ALPHABETICAL_ order! @@ -393,10 +409,12 @@ class VIEWS_EXPORT HWNDMessageHandler : void OnCommand(UINT notification_code, int command, HWND window); LRESULT OnCreate(CREATESTRUCT* create_struct); void OnDestroy(); - void OnDisplayChange(UINT bits_per_pixel, const CSize& screen_size); + void OnDisplayChange(UINT bits_per_pixel, const gfx::Size& screen_size); LRESULT OnDwmCompositionChanged(UINT msg, WPARAM w_param, LPARAM l_param); + void OnEnterMenuLoop(BOOL from_track_popup_menu); void OnEnterSizeMove(); LRESULT OnEraseBkgnd(HDC dc); + void OnExitMenuLoop(BOOL is_shortcut_menu); void OnExitSizeMove(); void OnGetMinMaxInfo(MINMAXINFO* minmax_info); LRESULT OnGetObject(UINT message, WPARAM w_param, LPARAM l_param); @@ -407,11 +425,11 @@ class VIEWS_EXPORT HWNDMessageHandler : void OnKillFocus(HWND focused_window); LRESULT OnMouseActivate(UINT message, WPARAM w_param, LPARAM l_param); LRESULT OnMouseRange(UINT message, WPARAM w_param, LPARAM l_param); - void OnMove(const CPoint& point); + void OnMove(const gfx::Point& point); void OnMoving(UINT param, const RECT* new_bounds); LRESULT OnNCActivate(UINT message, WPARAM w_param, LPARAM l_param); LRESULT OnNCCalcSize(BOOL mode, LPARAM l_param); - LRESULT OnNCHitTest(const CPoint& point); + LRESULT OnNCHitTest(const gfx::Point& point); void OnNCPaint(HRGN rgn); LRESULT OnNCUAHDrawCaption(UINT message, WPARAM w_param, LPARAM l_param); LRESULT OnNCUAHDrawFrame(UINT message, WPARAM w_param, LPARAM l_param); @@ -425,8 +443,8 @@ class VIEWS_EXPORT HWNDMessageHandler : LRESULT OnSetIcon(UINT size_type, HICON new_icon); LRESULT OnSetText(const wchar_t* text); void OnSettingChange(UINT flags, const wchar_t* section); - void OnSize(UINT param, const CSize& size); - void OnSysCommand(UINT notification_code, const CPoint& point); + void OnSize(UINT param, const gfx::Size& size); + void OnSysCommand(UINT notification_code, const gfx::Point& point); void OnThemeChanged(); LRESULT OnTouchEvent(UINT message, WPARAM w_param, LPARAM l_param); void OnWindowPosChanging(WINDOWPOS* window_pos); @@ -438,6 +456,28 @@ class VIEWS_EXPORT HWNDMessageHandler : // of a touch event. void HandleTouchEvents(const TouchEvents& touch_events); + // Resets the flag which indicates that we are in the context of a touch down + // event. + void ResetTouchDownContext(); + + // Helper to handle mouse events. + // The |message|, |w_param|, |l_param| parameters identify the Windows mouse + // message and its parameters respectively. + // The |track_mouse| parameter indicates if we should track the mouse. + LRESULT HandleMouseEventInternal(UINT message, + WPARAM w_param, + LPARAM l_param, + bool track_mouse); + + // Returns true if the mouse message passed in is an OS synthesized mouse + // message. + // |message| identifies the mouse message. + // |message_time| is the time when the message occurred. + // |l_param| indicates the location of the mouse message. + bool IsSynthesizedMouseMessage(unsigned int message, + int message_time, + LPARAM l_param); + HWNDMessageHandlerDelegate* delegate_; scoped_ptr<FullscreenHandler> fullscreen_handler_; @@ -449,10 +489,6 @@ class VIEWS_EXPORT HWNDMessageHandler : bool use_system_default_icon_; - // Whether the focus should be restored next time we get enabled. Needed to - // restore focus correctly when Windows modal dialogs are displayed. - bool restore_focus_when_enabled_; - // Whether all ancestors have been enabled. This is only used if is_modal_ is // true. bool restored_enabled_; @@ -535,6 +571,9 @@ class VIEWS_EXPORT HWNDMessageHandler : // Copy of custom window region specified via SetRegion(), if any. base::win::ScopedRegion custom_window_region_; + // If > 0 indicates a menu is running (we're showing a native menu). + int menu_depth_; + // A factory used to lookup appbar autohide edges. base::WeakPtrFactory<HWNDMessageHandler> autohide_factory_; @@ -547,14 +586,30 @@ class VIEWS_EXPORT HWNDMessageHandler : // Set to true if we are in the context of a sizing operation. bool in_size_loop_; + // Stores a pointer to the WindowEventTarget interface implemented by this + // class. Allows callers to retrieve the interface pointer. + scoped_ptr<ui::ViewProp> prop_window_target_; + + // Number of active touch down contexts. This is incremented on touch down + // events and decremented later using a delayed task. + // We need this to ignore WM_MOUSEACTIVATE messages generated in response to + // touch input. This is fine because activation still works correctly via + // native SetFocus calls invoked in the views code. + int touch_down_contexts_; + + // Time the last touch message was received. Used to flag mouse messages + // synthesized by Windows for touch which are not flagged by the OS as + // synthesized mouse messages. For more information please refer to + // the IsMouseEventFromTouch function. + static long last_touch_message_time_; + + // Time the last WM_MOUSEHWHEEL message is received. Please refer to the + // HandleMouseEventInternal function as to why this is needed. + long last_mouse_hwheel_time_; + DISALLOW_COPY_AND_ASSIGN(HWNDMessageHandler); }; -// This window property if set on the window does not activate the window for a -// touch based WM_MOUSEACTIVATE message. -const wchar_t kIgnoreTouchMouseActivateForWindow[] = - L"Chrome.IgnoreMouseActivate"; - } // namespace views #endif // UI_VIEWS_WIN_HWND_MESSAGE_HANDLER_H_ diff --git a/chromium/ui/views/win/hwnd_message_handler_delegate.h b/chromium/ui/views/win/hwnd_message_handler_delegate.h index e932d30c5e8..e17bf78eaa6 100644 --- a/chromium/ui/views/win/hwnd_message_handler_delegate.h +++ b/chromium/ui/views/win/hwnd_message_handler_delegate.h @@ -25,7 +25,6 @@ class TouchEvent; namespace views { class InputMethod; -class NativeWidgetWin; // Implemented by the object that uses the HWNDMessageHandler to handle // notifications from the underlying HWND and service requests for data. @@ -47,13 +46,6 @@ class VIEWS_EXPORT HWNDMessageHandlerDelegate { virtual bool WidgetSizeIsClientSize() const = 0; - // Returns true if the delegate has a focus saving mechanism that should be - // used when the window is activated and deactivated. - virtual bool CanSaveFocus() const = 0; - virtual void SaveFocusOnDeactivate() = 0; - virtual void RestoreFocusOnActivate() = 0; - virtual void RestoreFocusOnEnable() = 0; - // Returns true if the delegate represents a modal window. virtual bool IsModal() const = 0; @@ -137,7 +129,7 @@ class VIEWS_EXPORT HWNDMessageHandlerDelegate { // Called when the HWND is to be focused for the first time. This is called // when the window is shown for the first time. Returns true if the delegate // set focus and no default processing should be done by the message handler. - virtual bool HandleInitialFocus() = 0; + virtual bool HandleInitialFocus(ui::WindowShowState show_state) = 0; // Called when display settings are adjusted on the system. virtual void HandleDisplayChange() = 0; @@ -217,6 +209,9 @@ class VIEWS_EXPORT HWNDMessageHandlerDelegate { WPARAM w_param, LPARAM l_param) = 0; + // Invoked on entering/exiting a menu loop. + virtual void HandleMenuLoop(bool in_menu_loop) = 0; + // Catch-all message handling and filtering. Called before // HWNDMessageHandler's built-in handling, which may pre-empt some // expectations in Views/Aura if messages are consumed. Returns true if the @@ -238,6 +233,9 @@ class VIEWS_EXPORT HWNDMessageHandlerDelegate { // handled by the delegate. virtual bool HandleScrollEvent(const ui::ScrollEvent& event) = 0; + // Called when the window size is about to change. + virtual void HandleWindowSizeChanging() = 0; + protected: virtual ~HWNDMessageHandlerDelegate() {} }; diff --git a/chromium/ui/views/win/hwnd_util_aurawin.cc b/chromium/ui/views/win/hwnd_util_aurawin.cc index 9fc6f10d816..37dca58f774 100644 --- a/chromium/ui/views/win/hwnd_util_aurawin.cc +++ b/chromium/ui/views/win/hwnd_util_aurawin.cc @@ -4,8 +4,8 @@ #include "ui/views/win/hwnd_util.h" -#include "ui/aura/root_window.h" #include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" #include "ui/views/widget/widget.h" namespace views { @@ -20,21 +20,20 @@ HWND HWNDForWidget(const Widget* widget) { HWND HWNDForNativeView(const gfx::NativeView view) { return view && view->GetRootWindow() ? - view->GetDispatcher()->host()->GetAcceleratedWidget() : NULL; + view->GetHost()->GetAcceleratedWidget() : NULL; } HWND HWNDForNativeWindow(const gfx::NativeWindow window) { return window && window->GetRootWindow() ? - window->GetDispatcher()->host()->GetAcceleratedWidget() : NULL; + window->GetHost()->GetAcceleratedWidget() : NULL; } gfx::Rect GetWindowBoundsForClientBounds(View* view, const gfx::Rect& client_bounds) { DCHECK(view); - aura::WindowEventDispatcher* dispatcher = - view->GetWidget()->GetNativeWindow()->GetDispatcher(); - if (dispatcher) { - HWND hwnd = dispatcher->host()->GetAcceleratedWidget(); + aura::WindowTreeHost* host = view->GetWidget()->GetNativeWindow()->GetHost(); + if (host) { + HWND hwnd = host->GetAcceleratedWidget(); RECT rect = client_bounds.ToRECT(); DWORD style = ::GetWindowLong(hwnd, GWL_STYLE); DWORD ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); diff --git a/chromium/ui/views/win/hwnd_util_win.cc b/chromium/ui/views/win/hwnd_util_win.cc deleted file mode 100644 index abc05f8ec84..00000000000 --- a/chromium/ui/views/win/hwnd_util_win.cc +++ /dev/null @@ -1,39 +0,0 @@ -// 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/win/hwnd_util.h" - -#include "ui/views/widget/widget.h" - -namespace views { - -HWND HWNDForView(const View* view) { - return view->GetWidget() ? HWNDForWidget(view->GetWidget()) : NULL; -} - -// Returns the HWND associated with the specified widget. -HWND HWNDForWidget(const Widget* widget) { - return widget->GetNativeView(); -} - -HWND HWNDForNativeView(const gfx::NativeView view) { - return view; -} - -HWND HWNDForNativeWindow(const gfx::NativeWindow window) { - return window; -} - -gfx::Rect GetWindowBoundsForClientBounds(View* view, - const gfx::Rect& client_bounds) { - DCHECK(view); - HWND hwnd = view->GetWidget()->GetNativeWindow(); - RECT rect = client_bounds.ToRECT(); - DWORD style = ::GetWindowLong(hwnd, GWL_STYLE); - DWORD ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); - AdjustWindowRectEx(&rect, style, FALSE, ex_style); - return gfx::Rect(rect); -} - -} // namespace views diff --git a/chromium/ui/views/window/client_view.cc b/chromium/ui/views/window/client_view.cc index 7edc5f4d5b9..fb7d89fed18 100644 --- a/chromium/ui/views/window/client_view.cc +++ b/chromium/ui/views/window/client_view.cc @@ -5,7 +5,7 @@ #include "ui/views/window/client_view.h" #include "base/logging.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/base/hit_test.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" @@ -45,19 +45,19 @@ void ClientView::WidgetClosing() { /////////////////////////////////////////////////////////////////////////////// // ClientView, View overrides: -gfx::Size ClientView::GetPreferredSize() { +gfx::Size ClientView::GetPreferredSize() const { // |contents_view_| is allowed to be NULL up until the point where this view // is attached to a Container. return contents_view_ ? contents_view_->GetPreferredSize() : gfx::Size(); } -gfx::Size ClientView::GetMaximumSize() { +gfx::Size ClientView::GetMaximumSize() const { // |contents_view_| is allowed to be NULL up until the point where this view // is attached to a Container. return contents_view_ ? contents_view_->GetMaximumSize() : gfx::Size(); } -gfx::Size ClientView::GetMinimumSize() { +gfx::Size ClientView::GetMinimumSize() const { // |contents_view_| is allowed to be NULL up until the point where this view // is attached to a Container. return contents_view_ ? contents_view_->GetMinimumSize() : gfx::Size(); @@ -74,8 +74,8 @@ const char* ClientView::GetClassName() const { return kViewClassName; } -void ClientView::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_CLIENT; +void ClientView::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_CLIENT; } void ClientView::OnBoundsChanged(const gfx::Rect& previous_bounds) { diff --git a/chromium/ui/views/window/client_view.h b/chromium/ui/views/window/client_view.h index 8c890d70b7c..5a329aad567 100644 --- a/chromium/ui/views/window/client_view.h +++ b/chromium/ui/views/window/client_view.h @@ -57,15 +57,15 @@ class VIEWS_EXPORT ClientView : public View { virtual int NonClientHitTest(const gfx::Point& point); // Overridden from View: - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual gfx::Size GetMaximumSize() OVERRIDE; - virtual gfx::Size GetMinimumSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual gfx::Size GetMinimumSize() const OVERRIDE; + virtual gfx::Size GetMaximumSize() const OVERRIDE; virtual void Layout() OVERRIDE; virtual const char* GetClassName() const OVERRIDE; protected: // Overridden from View: - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE; virtual void ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) OVERRIDE; diff --git a/chromium/ui/views/window/custom_frame_view.cc b/chromium/ui/views/window/custom_frame_view.cc index bb27dc60ca5..a3fb3f7c96c 100644 --- a/chromium/ui/views/window/custom_frame_view.cc +++ b/chromium/ui/views/window/custom_frame_view.cc @@ -5,6 +5,7 @@ #include "ui/views/window/custom_frame_view.h" #include <algorithm> +#include <vector> #include "base/strings/utf_string_conversions.h" #include "grit/ui_resources.h" @@ -16,21 +17,19 @@ #include "ui/gfx/font.h" #include "ui/gfx/image/image.h" #include "ui/gfx/path.h" +#include "ui/gfx/rect.h" #include "ui/views/color_constants.h" #include "ui/views/controls/button/image_button.h" +#include "ui/views/views_delegate.h" +#include "ui/views/widget/native_widget_private.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" #include "ui/views/window/client_view.h" #include "ui/views/window/frame_background.h" +#include "ui/views/window/window_button_order_provider.h" #include "ui/views/window/window_resources.h" #include "ui/views/window/window_shape.h" -#if defined(USE_AURA) -#include "ui/views/widget/native_widget_aura.h" -#elif defined(OS_WIN) -#include "ui/views/widget/native_widget_win.h" -#endif - namespace views { namespace { @@ -65,19 +64,17 @@ const SkColor kDefaultColorFrame = SkColorSetRGB(66, 116, 201); const SkColor kDefaultColorFrameInactive = SkColorSetRGB(161, 182, 228); #endif -const gfx::Font& GetTitleFont() { - static gfx::Font* title_font = NULL; - if (!title_font) { -#if defined(USE_AURA) - title_font = new gfx::Font(NativeWidgetAura::GetWindowTitleFont()); -#elif defined(OS_WIN) - title_font = new gfx::Font(NativeWidgetWin::GetWindowTitleFont()); -#elif defined(OS_LINUX) - // TODO(ben): need to resolve what font this is. - title_font = new gfx::Font(); -#endif - } - return *title_font; +const gfx::FontList& GetTitleFontList() { + static const gfx::FontList title_font_list = + internal::NativeWidgetPrivate::GetWindowTitleFontList(); + return title_font_list; +} + +void LayoutButton(ImageButton* button, const gfx::Rect& bounds) { + button->SetVisible(true); + button->SetImageAlignment(ImageButton::ALIGN_LEFT, + ImageButton::ALIGN_BOTTOM); + button->SetBoundsRect(bounds); } } // namespace @@ -93,7 +90,9 @@ CustomFrameView::CustomFrameView() restore_button_(NULL), close_button_(NULL), should_show_maximize_button_(false), - frame_background_(new FrameBackground()) { + frame_background_(new FrameBackground()), + minimum_title_bar_x_(0), + maximum_title_bar_x_(-1) { } CustomFrameView::~CustomFrameView() { @@ -102,19 +101,12 @@ CustomFrameView::~CustomFrameView() { void CustomFrameView::Init(Widget* frame) { frame_ = frame; - close_button_ = new ImageButton(this); - close_button_->SetAccessibleName( - l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE)); - - // Close button images will be set in LayoutWindowControls(). - AddChildView(close_button_); - + close_button_ = InitWindowCaptionButton(IDS_APP_ACCNAME_CLOSE, + IDR_CLOSE, IDR_CLOSE_H, IDR_CLOSE_P); minimize_button_ = InitWindowCaptionButton(IDS_APP_ACCNAME_MINIMIZE, IDR_MINIMIZE, IDR_MINIMIZE_H, IDR_MINIMIZE_P); - maximize_button_ = InitWindowCaptionButton(IDS_APP_ACCNAME_MAXIMIZE, IDR_MAXIMIZE, IDR_MAXIMIZE_H, IDR_MAXIMIZE_P); - restore_button_ = InitWindowCaptionButton(IDS_APP_ACCNAME_RESTORE, IDR_RESTORE, IDR_RESTORE_H, IDR_RESTORE_P); @@ -137,8 +129,8 @@ gfx::Rect CustomFrameView::GetWindowBoundsForClientBounds( const gfx::Rect& client_bounds) const { int top_height = NonClientTopBorderHeight(); int border_thickness = NonClientBorderThickness(); - return gfx::Rect(std::max(0, client_bounds.x() - border_thickness), - std::max(0, client_bounds.y() - top_height), + return gfx::Rect(client_bounds.x() - border_thickness, + client_bounds.y() - top_height, client_bounds.width() + (2 * border_thickness), client_bounds.height() + top_height + border_thickness); } @@ -186,7 +178,7 @@ int CustomFrameView::NonClientHitTest(const gfx::Point& point) { void CustomFrameView::GetWindowMask(const gfx::Size& size, gfx::Path* window_mask) { DCHECK(window_mask); - if (frame_->IsMaximized()) + if (frame_->IsMaximized() || !ShouldShowTitleBarAndBorder()) return; GetDefaultWindowMask(size, window_mask); @@ -205,13 +197,17 @@ void CustomFrameView::UpdateWindowIcon() { } void CustomFrameView::UpdateWindowTitle() { - SchedulePaintInRect(title_bounds_); + if (frame_->widget_delegate()->ShouldShowWindowTitle()) + SchedulePaintInRect(title_bounds_); } /////////////////////////////////////////////////////////////////////////////// // CustomFrameView, View overrides: void CustomFrameView::OnPaint(gfx::Canvas* canvas) { + if (!ShouldShowTitleBarAndBorder()) + return; + if (frame_->IsMaximized()) PaintMaximizedFrameBorder(canvas); else @@ -222,22 +218,25 @@ void CustomFrameView::OnPaint(gfx::Canvas* canvas) { } void CustomFrameView::Layout() { - LayoutWindowControls(); - LayoutTitleBar(); + if (ShouldShowTitleBarAndBorder()) { + LayoutWindowControls(); + LayoutTitleBar(); + } + LayoutClientView(); } -gfx::Size CustomFrameView::GetPreferredSize() { +gfx::Size CustomFrameView::GetPreferredSize() const { return frame_->non_client_view()->GetWindowBoundsForClientBounds( gfx::Rect(frame_->client_view()->GetPreferredSize())).size(); } -gfx::Size CustomFrameView::GetMinimumSize() { +gfx::Size CustomFrameView::GetMinimumSize() const { return frame_->non_client_view()->GetWindowBoundsForClientBounds( gfx::Rect(frame_->client_view()->GetMinimumSize())).size(); } -gfx::Size CustomFrameView::GetMaximumSize() { +gfx::Size CustomFrameView::GetMaximumSize() const { gfx::Size max_size = frame_->client_view()->GetMaximumSize(); gfx::Size converted_size = frame_->non_client_view()->GetWindowBoundsForClientBounds( @@ -282,7 +281,11 @@ int CustomFrameView::NonClientTopBorderHeight() const { int CustomFrameView::CaptionButtonY() const { // Maximized buttons start at window top so that even if their images aren't // drawn flush with the screen edge, they still obey Fitts' Law. +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + return FrameBorderThickness(); +#else return frame_->IsMaximized() ? FrameBorderThickness() : kFrameShadowThickness; +#endif } int CustomFrameView::TitlebarBottomThickness() const { @@ -296,7 +299,7 @@ int CustomFrameView::IconSize() const { // size are increased. return GetSystemMetrics(SM_CYSMICON); #else - return std::max(GetTitleFont().GetHeight(), kIconMinimumSize); + return std::max(GetTitleFontList().GetHeight(), kIconMinimumSize); #endif } @@ -320,11 +323,24 @@ gfx::Rect CustomFrameView::IconBounds() const { // 3D edge (or nothing at all, for maximized windows) above; hence the +1. int y = unavailable_px_at_top + (NonClientTopBorderHeight() - unavailable_px_at_top - size - TitlebarBottomThickness() + 1) / 2; - return gfx::Rect(frame_thickness + kIconLeftSpacing, y, size, size); + return gfx::Rect(frame_thickness + kIconLeftSpacing + minimum_title_bar_x_, + y, size, size); +} + +bool CustomFrameView::ShouldShowTitleBarAndBorder() const { + if (frame_->IsFullscreen()) + return false; + + if (ViewsDelegate::views_delegate) { + return !ViewsDelegate::views_delegate->WindowManagerProvidesTitleBar( + frame_->IsMaximized()); + } + + return true; } bool CustomFrameView::ShouldShowClientEdge() const { - return !frame_->IsMaximized(); + return !frame_->IsMaximized() && ShouldShowTitleBarAndBorder(); } void CustomFrameView::PaintRestoredFrameBorder(gfx::Canvas* canvas) { @@ -374,13 +390,13 @@ void CustomFrameView::PaintTitleBar(gfx::Canvas* canvas) { // It seems like in some conditions we can be asked to paint after the window // that contains us is WM_DESTROYed. At this point, our delegate is NULL. The // correct long term fix may be to shut down the RootView in WM_DESTROY. - if (!delegate) + if (!delegate || !delegate->ShouldShowWindowTitle()) return; - canvas->DrawStringInt(delegate->GetWindowTitle(), GetTitleFont(), - SK_ColorWHITE, GetMirroredXForRect(title_bounds_), - title_bounds_.y(), title_bounds_.width(), - title_bounds_.height()); + gfx::Rect rect = title_bounds_; + rect.set_x(GetMirroredXForRect(title_bounds_)); + canvas->DrawStringRect(delegate->GetWindowTitle(), GetTitleFontList(), + SK_ColorWHITE, rect); } void CustomFrameView::PaintRestoredClientEdge(gfx::Canvas* canvas) { @@ -462,70 +478,68 @@ const gfx::ImageSkia* CustomFrameView::GetFrameImage() const { } void CustomFrameView::LayoutWindowControls() { - close_button_->SetImageAlignment(ImageButton::ALIGN_LEFT, - ImageButton::ALIGN_BOTTOM); + minimum_title_bar_x_ = 0; + maximum_title_bar_x_ = width(); + + if (bounds().IsEmpty()) + return; + int caption_y = CaptionButtonY(); bool is_maximized = frame_->IsMaximized(); // There should always be the same number of non-shadow pixels visible to the - // side of the caption buttons. In maximized mode we extend the rightmost - // button to the screen corner to obey Fitts' Law. - int right_extra_width = is_maximized ? + // side of the caption buttons. In maximized mode we extend the edge button + // to the screen corner to obey Fitts' Law. + int extra_width = is_maximized ? (kFrameBorderThickness - kFrameShadowThickness) : 0; - gfx::Size close_button_size = close_button_->GetPreferredSize(); - close_button_->SetBounds(width() - FrameBorderThickness() - - right_extra_width - close_button_size.width(), caption_y, - close_button_size.width() + right_extra_width, - close_button_size.height()); - - // When the window is restored, we show a maximized button; otherwise, we show - // a restore button. + int next_button_x = FrameBorderThickness(); + bool is_restored = !is_maximized && !frame_->IsMinimized(); ImageButton* invisible_button = is_restored ? restore_button_ : maximize_button_; invisible_button->SetVisible(false); - ImageButton* visible_button = is_restored ? maximize_button_ - : restore_button_; - FramePartImage normal_part, hot_part, pushed_part; - int next_button_x; - if (should_show_maximize_button_) { - visible_button->SetVisible(true); - visible_button->SetImageAlignment(ImageButton::ALIGN_LEFT, - ImageButton::ALIGN_BOTTOM); - gfx::Size visible_button_size = visible_button->GetPreferredSize(); - visible_button->SetBounds(close_button_->x() - visible_button_size.width(), - caption_y, visible_button_size.width(), - visible_button_size.height()); - next_button_x = visible_button->x(); - } else { - visible_button->SetVisible(false); - next_button_x = close_button_->x(); + WindowButtonOrderProvider* button_order = + WindowButtonOrderProvider::GetInstance(); + const std::vector<views::FrameButton>& leading_buttons = + button_order->leading_buttons(); + const std::vector<views::FrameButton>& trailing_buttons = + button_order->trailing_buttons(); + + ImageButton* button = NULL; + for (std::vector<views::FrameButton>::const_iterator it = + leading_buttons.begin(); it != leading_buttons.end(); ++it) { + button = GetImageButton(*it); + if (!button) + continue; + gfx::Rect target_bounds(gfx::Point(next_button_x, caption_y), + button->GetPreferredSize()); + if (it == leading_buttons.begin()) + target_bounds.set_width(target_bounds.width() + extra_width); + LayoutButton(button, target_bounds); + next_button_x += button->width(); + minimum_title_bar_x_ = std::min(width(), next_button_x); } - minimize_button_->SetVisible(true); - minimize_button_->SetImageAlignment(ImageButton::ALIGN_LEFT, - ImageButton::ALIGN_BOTTOM); - gfx::Size minimize_button_size = minimize_button_->GetPreferredSize(); - minimize_button_->SetBounds( - next_button_x - minimize_button_size.width(), caption_y, - minimize_button_size.width(), - minimize_button_size.height()); - - normal_part = IDR_CLOSE; - hot_part = IDR_CLOSE_H; - pushed_part = IDR_CLOSE_P; - - ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - - close_button_->SetImage(CustomButton::STATE_NORMAL, - rb.GetImageNamed(normal_part).ToImageSkia()); - close_button_->SetImage(CustomButton::STATE_HOVERED, - rb.GetImageNamed(hot_part).ToImageSkia()); - close_button_->SetImage(CustomButton::STATE_PRESSED, - rb.GetImageNamed(pushed_part).ToImageSkia()); + // Trailing buttions are laid out in a RTL fashion + next_button_x = width() - FrameBorderThickness(); + for (std::vector<views::FrameButton>::const_reverse_iterator it = + trailing_buttons.rbegin(); it != trailing_buttons.rend(); ++it) { + button = GetImageButton(*it); + if (!button) + continue; + gfx::Rect target_bounds(gfx::Point(next_button_x, caption_y), + button->GetPreferredSize()); + if (it == trailing_buttons.rbegin()) + target_bounds.set_width(target_bounds.width() + extra_width); + target_bounds.Offset(-target_bounds.width(), 0); + LayoutButton(button, target_bounds); + next_button_x = button->x(); + maximum_title_bar_x_ = std::max(minimum_title_bar_x_, next_button_x); + } } void CustomFrameView::LayoutTitleBar() { + DCHECK_GE(maximum_title_bar_x_, 0); // The window title position is calculated based on the icon position, even // when there is no icon. gfx::Rect icon_bounds(IconBounds()); @@ -533,10 +547,13 @@ void CustomFrameView::LayoutTitleBar() { if (show_window_icon) window_icon_->SetBoundsRect(icon_bounds); + if (!frame_->widget_delegate()->ShouldShowWindowTitle()) + return; + // The offset between the window left edge and the title text. int title_x = show_window_icon ? icon_bounds.right() + kTitleIconOffsetX : icon_bounds.x(); - int title_height = GetTitleFont().GetHeight(); + int title_height = GetTitleFontList().GetHeight(); // We bias the title position so that when the difference between the icon and // title heights is odd, the extra pixel of the title is above the vertical // midline rather than below. This compensates for how the icon is already @@ -544,11 +561,16 @@ void CustomFrameView::LayoutTitleBar() { // title from overlapping the 3D edge at the bottom of the titlebar. title_bounds_.SetRect(title_x, icon_bounds.y() + ((icon_bounds.height() - title_height - 1) / 2), - std::max(0, minimize_button_->x() - kTitleCaptionSpacing - + std::max(0, maximum_title_bar_x_ - kTitleCaptionSpacing - title_x), title_height); } void CustomFrameView::LayoutClientView() { + if (!ShouldShowTitleBarAndBorder()) { + client_view_bounds_ = bounds(); + return; + } + int top_height = NonClientTopBorderHeight(); int border_thickness = NonClientBorderThickness(); client_view_bounds_.SetRect(border_thickness, top_height, @@ -574,4 +596,31 @@ ImageButton* CustomFrameView::InitWindowCaptionButton( return button; } +ImageButton* CustomFrameView::GetImageButton(views::FrameButton frame_button) { + ImageButton* button = NULL; + switch (frame_button) { + case views::FRAME_BUTTON_MINIMIZE: { + button = minimize_button_; + break; + } + case views::FRAME_BUTTON_MAXIMIZE: { + bool is_restored = !frame_->IsMaximized() && !frame_->IsMinimized(); + button = is_restored ? maximize_button_ : restore_button_; + if (!should_show_maximize_button_) { + // If we should not show the maximize/restore button, then we return + // NULL as we don't want this button to become visible and to be laid + // out. + button->SetVisible(false); + return NULL; + } + break; + } + case views::FRAME_BUTTON_CLOSE: { + button = close_button_; + break; + } + } + return button; +} + } // namespace views diff --git a/chromium/ui/views/window/custom_frame_view.h b/chromium/ui/views/window/custom_frame_view.h index 8c96df7fc9f..fe5a3e4c7bd 100644 --- a/chromium/ui/views/window/custom_frame_view.h +++ b/chromium/ui/views/window/custom_frame_view.h @@ -9,6 +9,7 @@ #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" #include "ui/views/controls/button/button.h" +#include "ui/views/window/frame_buttons.h" #include "ui/views/window/non_client_view.h" namespace gfx { @@ -29,8 +30,8 @@ class Widget; // rendering the non-standard window caption, border, and controls. // //////////////////////////////////////////////////////////////////////////////// -class CustomFrameView : public NonClientFrameView, - public ButtonListener { +class VIEWS_EXPORT CustomFrameView : public NonClientFrameView, + public ButtonListener { public: CustomFrameView(); virtual ~CustomFrameView(); @@ -51,14 +52,16 @@ class CustomFrameView : public NonClientFrameView, // Overridden from View: virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; virtual void Layout() OVERRIDE; - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual gfx::Size GetMinimumSize() OVERRIDE; - virtual gfx::Size GetMaximumSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual gfx::Size GetMinimumSize() const OVERRIDE; + virtual gfx::Size GetMaximumSize() const OVERRIDE; // Overridden from ButtonListener: virtual void ButtonPressed(Button* sender, const ui::Event& event) OVERRIDE; private: + friend class CustomFrameViewTest; + // Returns the thickness of the border that makes up the window frame edges. // This does not include any client edge. int FrameBorderThickness() const; @@ -86,6 +89,10 @@ class CustomFrameView : public NonClientFrameView, // there was one). gfx::Rect IconBounds() const; + // Returns true if the title bar, caption buttons, and frame border should be + // drawn. If false, the client view occupies the full area of this view. + bool ShouldShowTitleBarAndBorder() const; + // Returns true if the client edge should be drawn. This is true if // the window is not maximized. bool ShouldShowClientEdge() const; @@ -100,8 +107,13 @@ class CustomFrameView : public NonClientFrameView, SkColor GetFrameColor() const; const gfx::ImageSkia* GetFrameImage() const; - // Layout various sub-components of this view. + // Performs the layout for the window control buttons based on the + // configuration specified in WindowButtonOrderProvider. The sizing and + // positions of the buttons affects LayoutTitleBar, call this beforehand. void LayoutWindowControls(); + + // Calculations depend on the positions of the window controls. Always call + // LayoutWindowControls beforehand. void LayoutTitleBar(); void LayoutClientView(); @@ -112,6 +124,10 @@ class CustomFrameView : public NonClientFrameView, int hot_image_id, int pushed_image_id); + // Returns the window caption button for the given FrameButton type, if it + // should be visible. Otherwise NULL. + ImageButton* GetImageButton(views::FrameButton button); + // The bounds of the client view, in this view's coordinates. gfx::Rect client_view_bounds_; @@ -136,6 +152,11 @@ class CustomFrameView : public NonClientFrameView, // Background painter for the window frame. scoped_ptr<FrameBackground> frame_background_; + // The horizontal boundaries for the title bar to layout within. Restricted + // by the space used by the leading and trailing buttons. + int minimum_title_bar_x_; + int maximum_title_bar_x_; + DISALLOW_COPY_AND_ASSIGN(CustomFrameView); }; diff --git a/chromium/ui/views/window/custom_frame_view_unittest.cc b/chromium/ui/views/window/custom_frame_view_unittest.cc new file mode 100644 index 00000000000..26711190214 --- /dev/null +++ b/chromium/ui/views/window/custom_frame_view_unittest.cc @@ -0,0 +1,252 @@ +// Copyright 2014 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/window/custom_frame_view.h" + +#include <vector> + +#include "ui/views/controls/button/image_button.h" +#include "ui/views/test/views_test_base.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" +#include "ui/views/window/window_button_order_provider.h" + +namespace views { + +namespace { + +// Allows for the control of whether or not the widget can maximize or not. +// This can be set after initial setup in order to allow testing of both forms +// of delegates. By default this can maximize. +class MaximizeStateControlDelegate : public WidgetDelegateView { + public: + MaximizeStateControlDelegate() : can_maximize_(true) {} + virtual ~MaximizeStateControlDelegate() {} + + void set_can_maximize(bool can_maximize) { + can_maximize_ = can_maximize; + } + + // WidgetDelegate: + virtual bool CanMaximize() const OVERRIDE { return can_maximize_; } + + private: + bool can_maximize_; + + DISALLOW_COPY_AND_ASSIGN(MaximizeStateControlDelegate); +}; + +} // namespace + +class CustomFrameViewTest : public ViewsTestBase { + public: + CustomFrameViewTest() {} + virtual ~CustomFrameViewTest() {} + + CustomFrameView* custom_frame_view() { + return custom_frame_view_; + } + + MaximizeStateControlDelegate* maximize_state_control_delegate() { + return maximize_state_control_delegate_; + } + + Widget* widget() { + return widget_; + } + + // ViewsTestBase: + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; + + protected: + const std::vector<views::FrameButton>& leading_buttons() { + return WindowButtonOrderProvider::GetInstance()->leading_buttons(); + } + + const std::vector<views::FrameButton>& trailing_buttons() { + return WindowButtonOrderProvider::GetInstance()->trailing_buttons(); + } + + ImageButton* minimize_button() { + return custom_frame_view_->minimize_button_; + } + + ImageButton* maximize_button() { + return custom_frame_view_->maximize_button_; + } + + ImageButton* restore_button() { + return custom_frame_view_->restore_button_; + } + + ImageButton* close_button() { + return custom_frame_view_->close_button_; + } + + gfx::Rect title_bounds() { + return custom_frame_view_->title_bounds_; + } + + void SetWindowButtonOrder( + const std::vector<views::FrameButton> leading_buttons, + const std::vector<views::FrameButton> trailing_buttons); + + private: + // Parent container for |custom_frame_view_| + Widget* widget_; + + // Owned by |widget_| + CustomFrameView* custom_frame_view_; + + // Delegate of |widget_| which controls maximizing + MaximizeStateControlDelegate* maximize_state_control_delegate_; + + DISALLOW_COPY_AND_ASSIGN(CustomFrameViewTest); +}; + +void CustomFrameViewTest::SetUp() { + ViewsTestBase::SetUp(); + + maximize_state_control_delegate_ = new MaximizeStateControlDelegate; + widget_ = new Widget; + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); + params.delegate = maximize_state_control_delegate_; + params.remove_standard_frame = true; + widget_->Init(params); + + custom_frame_view_ = new CustomFrameView; + widget_->non_client_view()->SetFrameView(custom_frame_view_); +} + +void CustomFrameViewTest::TearDown() { + widget_->CloseNow(); + + ViewsTestBase::TearDown(); +} + +void CustomFrameViewTest::SetWindowButtonOrder( + const std::vector<views::FrameButton> leading_buttons, + const std::vector<views::FrameButton> trailing_buttons) { + WindowButtonOrderProvider::GetInstance()-> + SetWindowButtonOrder(leading_buttons, trailing_buttons); +} + +// Tests that there is a default button ordering before initialization causes +// a configuration file check. +TEST_F(CustomFrameViewTest, DefaultButtons) { + const std::vector<views::FrameButton>& trailing = trailing_buttons(); + EXPECT_EQ(trailing.size(), 3u); + EXPECT_TRUE(leading_buttons().empty()); + EXPECT_EQ(trailing[0], FRAME_BUTTON_MINIMIZE); + EXPECT_EQ(trailing[1], FRAME_BUTTON_MAXIMIZE); + EXPECT_EQ(trailing[2], FRAME_BUTTON_CLOSE); +} + +// Tests that layout places the buttons in order, that the restore button is +// hidden and the buttons are placed after the title. +TEST_F(CustomFrameViewTest, DefaultButtonLayout) { + Widget* parent = widget(); + CustomFrameView* view = custom_frame_view(); + view->Init(parent); + parent->SetBounds(gfx::Rect(0, 0, 300, 100)); + parent->Show(); + + EXPECT_LT(minimize_button()->x(), maximize_button()->x()); + EXPECT_LT(maximize_button()->x(), close_button()->x()); + EXPECT_FALSE(restore_button()->visible()); + + EXPECT_GT(minimize_button()->x(), + title_bounds().x() + title_bounds().width()); +} + +// Tests that setting the buttons to leading places them before the title. +TEST_F(CustomFrameViewTest, LeadingButtonLayout) { + Widget* parent = widget(); + CustomFrameView* view = custom_frame_view(); + + std::vector<views::FrameButton> leading; + leading.push_back(views::FRAME_BUTTON_CLOSE); + leading.push_back(views::FRAME_BUTTON_MINIMIZE); + leading.push_back(views::FRAME_BUTTON_MAXIMIZE); + + std::vector<views::FrameButton> trailing; + + SetWindowButtonOrder(leading, trailing); + + view->Init(parent); + parent->SetBounds(gfx::Rect(0, 0, 300, 100)); + parent->Show(); + EXPECT_LT(close_button()->x(), minimize_button()->x()); + EXPECT_LT(minimize_button()->x(), maximize_button()->x()); + EXPECT_FALSE(restore_button()->visible()); + EXPECT_LT(maximize_button()->x() + maximize_button()->width(), + title_bounds().x()); +} + +// Tests that layouts occuring while maximized swap the maximize button for the +// restore button +TEST_F(CustomFrameViewTest, MaximizeRevealsRestoreButton) { + Widget* parent = widget(); + CustomFrameView* view = custom_frame_view(); + view->Init(parent); + parent->SetBounds(gfx::Rect(0, 0, 300, 100)); + parent->Show(); + + ASSERT_FALSE(restore_button()->visible()); + ASSERT_TRUE(maximize_button()->visible()); + + parent->Maximize(); + view->Layout(); + + EXPECT_TRUE(restore_button()->visible()); + EXPECT_FALSE(maximize_button()->visible()); +} + +// Tests that when the parent cannot maximize that the maximize button is not +// visible +TEST_F(CustomFrameViewTest, CannotMaximizeHidesButton) { + Widget* parent = widget(); + CustomFrameView* view = custom_frame_view(); + MaximizeStateControlDelegate* delegate = maximize_state_control_delegate(); + delegate->set_can_maximize(false); + + view->Init(parent); + parent->SetBounds(gfx::Rect(0, 0, 300, 100)); + parent->Show(); + + EXPECT_FALSE(restore_button()->visible()); + EXPECT_FALSE(maximize_button()->visible()); +} + +// Tests that when maximized that the edge button has an increased width. +TEST_F(CustomFrameViewTest, LargerEdgeButtonsWhenMaximized) { + Widget* parent = widget(); + CustomFrameView* view = custom_frame_view(); + + // Custom ordering to have a button on each edge. + std::vector<views::FrameButton> leading; + leading.push_back(views::FRAME_BUTTON_CLOSE); + leading.push_back(views::FRAME_BUTTON_MAXIMIZE); + std::vector<views::FrameButton> trailing; + trailing.push_back(views::FRAME_BUTTON_MINIMIZE); + SetWindowButtonOrder(leading, trailing); + + view->Init(parent); + parent->SetBounds(gfx::Rect(0, 0, 300, 100)); + parent->Show(); + + gfx::Rect close_button_initial_bounds = close_button()->bounds(); + gfx::Rect minimize_button_initial_bounds = minimize_button()->bounds(); + + parent->Maximize(); + view->Layout(); + + EXPECT_GT(close_button()->bounds().width(), + close_button_initial_bounds.width()); + EXPECT_GT(minimize_button()->bounds().width(), + minimize_button_initial_bounds.width()); +} + +} // namespace views diff --git a/chromium/ui/views/window/dialog_client_view.cc b/chromium/ui/views/window/dialog_client_view.cc index bfb870ed50b..172ca0fde3d 100644 --- a/chromium/ui/views/window/dialog_client_view.cc +++ b/chromium/ui/views/window/dialog_client_view.cc @@ -22,12 +22,30 @@ namespace { // conflict with other groups that could be in the dialog content. const int kButtonGroup = 6666; +#if defined(OS_WIN) || defined(OS_CHROMEOS) +const bool kIsOkButtonOnLeftSide = true; +#else +const bool kIsOkButtonOnLeftSide = false; +#endif + // Returns true if the given view should be shown (i.e. exists and is // visible). bool ShouldShow(View* view) { return view && view->visible(); } +// Do the layout for a button. +void LayoutButton(LabelButton* button, gfx::Rect* row_bounds) { + if (!button) + return; + + const gfx::Size size = button->GetPreferredSize(); + row_bounds->set_width(row_bounds->width() - size.width()); + button->SetBounds(row_bounds->right(), row_bounds->y(), + size.width(), row_bounds->height()); + row_bounds->set_width(row_bounds->width() - kRelatedButtonHSpacing); +} + } // namespace /////////////////////////////////////////////////////////////////////////////// @@ -157,7 +175,7 @@ void DialogClientView::OnDidChangeFocus(View* focused_before, //////////////////////////////////////////////////////////////////////////////// // DialogClientView, View overrides: -gfx::Size DialogClientView::GetPreferredSize() { +gfx::Size DialogClientView::GetPreferredSize() const { // Initialize the size to fit the buttons and extra view row. gfx::Size size( (ok_button_ ? ok_button_->GetPreferredSize().width() : 0) + @@ -213,19 +231,12 @@ void DialogClientView::Layout() { const int height = GetButtonsAndExtraViewRowHeight(); gfx::Rect row_bounds(bounds.x(), bounds.bottom() - height, bounds.width(), height); - if (cancel_button_) { - const gfx::Size size = cancel_button_->GetPreferredSize(); - row_bounds.set_width(row_bounds.width() - size.width()); - cancel_button_->SetBounds(row_bounds.right(), row_bounds.y(), - size.width(), height); - row_bounds.set_width(row_bounds.width() - kRelatedButtonHSpacing); - } - if (ok_button_) { - const gfx::Size size = ok_button_->GetPreferredSize(); - row_bounds.set_width(row_bounds.width() - size.width()); - ok_button_->SetBounds(row_bounds.right(), row_bounds.y(), - size.width(), height); - row_bounds.set_width(row_bounds.width() - kRelatedButtonHSpacing); + if (kIsOkButtonOnLeftSide) { + LayoutButton(cancel_button_, &row_bounds); + LayoutButton(ok_button_, &row_bounds); + } else { + LayoutButton(ok_button_, &row_bounds); + LayoutButton(cancel_button_, &row_bounds); } if (extra_view_) { row_bounds.set_width(std::min(row_bounds.width(), @@ -254,13 +265,6 @@ void DialogClientView::ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) { ClientView::ViewHierarchyChanged(details); if (details.is_add && details.child == this) { - // The old dialog style needs an explicit background color, while the new - // dialog style simply inherits the bubble's frame view color. - const DialogDelegate* dialog = GetDialogDelegate(); - if (dialog && !dialog->UseNewStyleForThisDialog()) - set_background(views::Background::CreateSolidBackground(GetNativeTheme()-> - GetSystemColor(ui::NativeTheme::kColorId_DialogBackground))); - focus_manager_ = GetFocusManager(); if (focus_manager_) GetFocusManager()->AddFocusChangeListener(this); @@ -293,6 +297,17 @@ void DialogClientView::NativeViewHierarchyChanged() { } } +void DialogClientView::OnNativeThemeChanged(const ui::NativeTheme* theme) { + // The old dialog style needs an explicit background color, while the new + // dialog style simply inherits the bubble's frame view color. + const DialogDelegate* dialog = GetDialogDelegate(); + + if (dialog && !dialog->UseNewStyleForThisDialog()) { + set_background(views::Background::CreateSolidBackground(GetNativeTheme()-> + GetSystemColor(ui::NativeTheme::kColorId_DialogBackground))); + } +} + //////////////////////////////////////////////////////////////////////////////// // DialogClientView, ButtonListener implementation: @@ -359,7 +374,7 @@ void DialogClientView::ChildVisibilityChanged(View* child) { // DialogClientView, private: LabelButton* DialogClientView::CreateDialogButton(ui::DialogButton type) { - const string16 title = GetDialogDelegate()->GetDialogButtonLabel(type); + const base::string16 title = GetDialogDelegate()->GetDialogButtonLabel(type); LabelButton* button = NULL; if (GetDialogDelegate()->UseNewStyleForThisDialog() && GetDialogDelegate()->GetDefaultDialogButton() == type && @@ -367,7 +382,7 @@ LabelButton* DialogClientView::CreateDialogButton(ui::DialogButton type) { button = new BlueButton(this, title); } else { button = new LabelButton(this, title); - button->SetStyle(Button::STYLE_NATIVE_TEXTBUTTON); + button->SetStyle(Button::STYLE_BUTTON); } button->SetFocusable(true); diff --git a/chromium/ui/views/window/dialog_client_view.h b/chromium/ui/views/window/dialog_client_view.h index d780806094c..1e8ced11c77 100644 --- a/chromium/ui/views/window/dialog_client_view.h +++ b/chromium/ui/views/window/dialog_client_view.h @@ -57,12 +57,13 @@ class VIEWS_EXPORT DialogClientView : public ClientView, View* focused_now) OVERRIDE; // View implementation: - virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; virtual void Layout() OVERRIDE; virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE; virtual void ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) OVERRIDE; virtual void NativeViewHierarchyChanged() OVERRIDE; + virtual void OnNativeThemeChanged(const ui::NativeTheme* theme) OVERRIDE; // ButtonListener implementation: virtual void ButtonPressed(Button* sender, const ui::Event& event) OVERRIDE; diff --git a/chromium/ui/views/window/dialog_delegate.cc b/chromium/ui/views/window/dialog_delegate.cc index cb5374acf55..5ec29a216a9 100644 --- a/chromium/ui/views/window/dialog_delegate.cc +++ b/chromium/ui/views/window/dialog_delegate.cc @@ -14,8 +14,8 @@ #include "ui/views/widget/widget_observer.h" #include "ui/views/window/dialog_client_view.h" -#if defined(USE_AURA) -#include "ui/views/corewm/shadow_types.h" +#if defined(OS_WIN) +#include "ui/base/win/shell.h" #endif namespace views { @@ -23,23 +23,42 @@ namespace views { //////////////////////////////////////////////////////////////////////////////// // DialogDelegate: +DialogDelegate::DialogDelegate() : supports_new_style_(true) { +} + DialogDelegate::~DialogDelegate() { } // static -Widget* DialogDelegate::CreateDialogWidget(DialogDelegate* dialog, - gfx::NativeWindow context, - gfx::NativeWindow parent) { +Widget* DialogDelegate::CreateDialogWidget(WidgetDelegate* delegate, + gfx::NativeView context, + gfx::NativeView parent) { views::Widget* widget = new views::Widget; views::Widget::InitParams params; - params.delegate = dialog; + params.delegate = delegate; + DialogDelegate* dialog = delegate->AsDialogDelegate(); + +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + // The new style doesn't support unparented dialogs on Linux desktop. + if (dialog) + dialog->supports_new_style_ &= parent != NULL; +#elif defined(OS_WIN) + // The new style doesn't support unparented dialogs on Windows Classic themes. + if (dialog && !ui::win::IsAeroGlassEnabled()) + dialog->supports_new_style_ &= parent != NULL; +#endif + if (!dialog || dialog->UseNewStyleForThisDialog()) { params.opacity = Widget::InitParams::TRANSLUCENT_WINDOW; params.remove_standard_frame = true; + // The bubble frame includes its own shadow; remove any native shadowing. + params.shadow_type = views::Widget::InitParams::SHADOW_TYPE_NONE; } params.context = context; params.parent = parent; - params.top_level = true; + // Web-modal (ui::MODAL_TYPE_CHILD) dialogs with parents are marked as child + // widgets to prevent top-level window behavior (independent movement, etc). + params.child = parent && (delegate->GetModalType() == ui::MODAL_TYPE_CHILD); widget->Init(params); return widget; } @@ -154,43 +173,22 @@ NonClientFrameView* DialogDelegate::CreateNonClientFrameView(Widget* widget) { // static NonClientFrameView* DialogDelegate::CreateDialogFrameView(Widget* widget) { - return CreateDialogFrameView(widget, false); -} - -// static -NonClientFrameView* DialogDelegate::CreateDialogFrameView( - Widget* widget, - bool force_opaque_border) { BubbleFrameView* frame = new BubbleFrameView(gfx::Insets()); - const SkColor color = widget->GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_DialogBackground); - if (force_opaque_border) { - frame->SetBubbleBorder(new BubbleBorder( - BubbleBorder::NONE, - BubbleBorder::NO_SHADOW_OPAQUE_BORDER, - color)); - } else { - frame->SetBubbleBorder(new BubbleBorder(BubbleBorder::FLOAT, - BubbleBorder::SMALL_SHADOW, - color)); - } + scoped_ptr<BubbleBorder> border(new BubbleBorder( + BubbleBorder::FLOAT, BubbleBorder::SMALL_SHADOW, SK_ColorRED)); + border->set_use_theme_background_color(true); + frame->SetBubbleBorder(border.Pass()); DialogDelegate* delegate = widget->widget_delegate()->AsDialogDelegate(); if (delegate) { View* titlebar_view = delegate->CreateTitlebarExtraView(); if (titlebar_view) frame->SetTitlebarExtraView(titlebar_view); } - if (force_opaque_border) - widget->set_frame_type(views::Widget::FRAME_TYPE_FORCE_CUSTOM); -#if defined(USE_AURA) - // TODO(msw): Add a matching shadow type and remove the bubble frame border? - corewm::SetShadowType(widget->GetNativeWindow(), corewm::SHADOW_TYPE_NONE); -#endif return frame; } bool DialogDelegate::UseNewStyleForThisDialog() const { - return true; + return supports_new_style_; } const DialogClientView* DialogDelegate::GetDialogClientView() const { @@ -201,8 +199,8 @@ DialogClientView* DialogDelegate::GetDialogClientView() { return GetWidget()->client_view()->AsDialogClientView(); } -ui::AccessibilityTypes::Role DialogDelegate::GetAccessibleWindowRole() const { - return ui::AccessibilityTypes::ROLE_DIALOG; +ui::AXRole DialogDelegate::GetAccessibleWindowRole() const { + return ui::AX_ROLE_DIALOG; } //////////////////////////////////////////////////////////////////////////////// diff --git a/chromium/ui/views/window/dialog_delegate.h b/chromium/ui/views/window/dialog_delegate.h index 8e8948de427..91fff1e51fb 100644 --- a/chromium/ui/views/window/dialog_delegate.h +++ b/chromium/ui/views/window/dialog_delegate.h @@ -7,7 +7,7 @@ #include "base/compiler_specific.h" #include "base/strings/string16.h" -#include "ui/base/accessibility/accessibility_types.h" +#include "ui/accessibility/ax_enums.h" #include "ui/base/models/dialog_model.h" #include "ui/base/ui_base_types.h" #include "ui/views/widget/widget_delegate.h" @@ -29,12 +29,13 @@ class DialogClientView; class VIEWS_EXPORT DialogDelegate : public ui::DialogModel, public WidgetDelegate { public: + DialogDelegate(); virtual ~DialogDelegate(); - // Create a |dialog| window Widget with the specified |context| or |parent|. - static Widget* CreateDialogWidget(DialogDelegate* dialog, - gfx::NativeWindow context, - gfx::NativeWindow parent); + // Create a dialog widget with the specified |context| or |parent|. + static Widget* CreateDialogWidget(WidgetDelegate* delegate, + gfx::NativeView context, + gfx::NativeView parent); // Override this function to display an extra view adjacent to the buttons. // Overrides may construct the view; this will only be called once per dialog. @@ -91,14 +92,6 @@ class VIEWS_EXPORT DialogDelegate : public ui::DialogModel, // Create a frame view using the new dialog style. static NonClientFrameView* CreateDialogFrameView(Widget* widget); - // The semi-transparent border and shadow of the new style frame view does not - // work on child windows under Views/Win32. This is a kludge to get a - // reasonable-looking opaque border for the dialog. Note that this does not - // support arrows. - // - // TODO(wittman): Remove once WinAura is in place. - static NonClientFrameView* CreateDialogFrameView(Widget* widget, - bool force_opaque_border); // Returns whether this particular dialog should use the new dialog style. virtual bool UseNewStyleForThisDialog() const; @@ -113,7 +106,11 @@ class VIEWS_EXPORT DialogDelegate : public ui::DialogModel, protected: // Overridden from WidgetDelegate: - virtual ui::AccessibilityTypes::Role GetAccessibleWindowRole() const OVERRIDE; + virtual ui::AXRole GetAccessibleWindowRole() const OVERRIDE; + + private: + // A flag indicating whether this dialog supports the new style. + bool supports_new_style_; }; // A DialogDelegate implementation that is-a View. Used to override GetWidget() diff --git a/chromium/ui/views/window/dialog_delegate_unittest.cc b/chromium/ui/views/window/dialog_delegate_unittest.cc index f56073affac..fedb6983eb2 100644 --- a/chromium/ui/views/window/dialog_delegate_unittest.cc +++ b/chromium/ui/views/window/dialog_delegate_unittest.cc @@ -37,8 +37,11 @@ class TestDialog : public DialogDelegateView, public ButtonListener { } // DialogDelegateView overrides: - virtual gfx::Size GetPreferredSize() OVERRIDE { return gfx::Size(200, 200); } - virtual string16 GetWindowTitle() const OVERRIDE { return title_; } + virtual gfx::Size GetPreferredSize() const OVERRIDE { + return gfx::Size(200, 200); + } + virtual base::string16 GetWindowTitle() const OVERRIDE { return title_; } + virtual bool UseNewStyleForThisDialog() const OVERRIDE { return true; } // ButtonListener override: virtual void ButtonPressed(Button* sender, const ui::Event& event) OVERRIDE { @@ -73,7 +76,7 @@ class TestDialog : public DialogDelegateView, public ButtonListener { GetWidget()->Close(); } - void set_title(const string16& title) { title_ = title; } + void set_title(const base::string16& title) { title_ = title; } private: bool canceled_; @@ -81,7 +84,7 @@ class TestDialog : public DialogDelegateView, public ButtonListener { // Prevent the dialog from closing, for repeated ok and cancel button clicks. bool closeable_; Button* last_pressed_button_; - string16 title_; + base::string16 title_; DISALLOW_COPY_AND_ASSIGN(TestDialog); }; @@ -121,21 +124,21 @@ TEST_F(DialogTest, DefaultButtons) { dialog()->PressEnterAndCheckStates(ok_button); // Focus another button in the dialog, it should become the default. - LabelButton* button_1 = new LabelButton(dialog(), string16()); + LabelButton* button_1 = new LabelButton(dialog(), base::string16()); client_view->AddChildView(button_1); client_view->OnWillChangeFocus(ok_button, button_1); EXPECT_TRUE(button_1->is_default()); dialog()->PressEnterAndCheckStates(button_1); // Focus a Checkbox (not a push button), OK should become the default again. - Checkbox* checkbox = new Checkbox(string16()); + Checkbox* checkbox = new Checkbox(base::string16()); client_view->AddChildView(checkbox); client_view->OnWillChangeFocus(button_1, checkbox); EXPECT_FALSE(button_1->is_default()); dialog()->PressEnterAndCheckStates(ok_button); // Focus yet another button in the dialog, it should become the default. - LabelButton* button_2 = new LabelButton(dialog(), string16()); + LabelButton* button_2 = new LabelButton(dialog(), base::string16()); client_view->AddChildView(button_2); client_view->OnWillChangeFocus(checkbox, button_2); EXPECT_FALSE(button_1->is_default()); @@ -205,21 +208,22 @@ TEST_F(DialogTest, HitTest) { } } -TEST_F(DialogTest, InitialBoundsAccommodateTitle) { - TestDialog* titled_dialog(new TestDialog()); - titled_dialog->set_title(ASCIIToUTF16("Title")); - DialogDelegate::CreateDialogWidget(titled_dialog, GetContext(), NULL); +TEST_F(DialogTest, BoundsAccommodateTitle) { + TestDialog* dialog2(new TestDialog()); + dialog2->set_title(base::ASCIIToUTF16("Title")); + DialogDelegate::CreateDialogWidget(dialog2, GetContext(), NULL); // Titled dialogs have taller initial frame bounds than untitled dialogs. - EXPECT_GT(titled_dialog->GetWidget()->GetWindowBoundsInScreen().height(), - dialog()->GetWidget()->GetWindowBoundsInScreen().height()); + View* frame1 = dialog()->GetWidget()->non_client_view()->frame_view(); + View* frame2 = dialog2->GetWidget()->non_client_view()->frame_view(); + EXPECT_LT(frame1->GetPreferredSize().height(), + frame2->GetPreferredSize().height()); - // Giving the default test dialog a title will make the bounds the same. - dialog()->set_title(ASCIIToUTF16("Title")); + // Giving the default test dialog a title will yield the same bounds. + dialog()->set_title(base::ASCIIToUTF16("Title")); dialog()->GetWidget()->UpdateWindowTitle(); - View* frame = dialog()->GetWidget()->non_client_view()->frame_view(); - EXPECT_EQ(titled_dialog->GetWidget()->GetWindowBoundsInScreen().height(), - frame->GetPreferredSize().height()); + EXPECT_EQ(frame1->GetPreferredSize().height(), + frame2->GetPreferredSize().height()); } } // namespace views diff --git a/chromium/ui/views/window/frame_background.cc b/chromium/ui/views/window/frame_background.cc index 111ac257814..f9e8293c0db 100644 --- a/chromium/ui/views/window/frame_background.cc +++ b/chromium/ui/views/window/frame_background.cc @@ -26,10 +26,7 @@ FrameBackground::FrameBackground() top_right_corner_(NULL), bottom_left_corner_(NULL), bottom_right_corner_(NULL), - maximized_top_left_(NULL), - maximized_top_right_(NULL), - maximized_top_offset_(0), - theme_background_y_(0) { + maximized_top_inset_(0) { } FrameBackground::~FrameBackground() { @@ -124,41 +121,25 @@ void FrameBackground::PaintRestored(gfx::Canvas* canvas, View* view) const { } void FrameBackground::PaintMaximized(gfx::Canvas* canvas, View* view) const { - // We will be painting from top_offset to top_offset + theme_frame_height. If - // this is less than top_area_height_, we need to paint the frame color - // to fill in the area beneath the image. - // TODO(jamescook): I'm not sure this is correct, as it doesn't seem to fully - // account for the top_offset, but this is how it worked before. - int theme_frame_bottom = maximized_top_offset_ + theme_image_->height(); + // We will be painting from -|maximized_top_inset_| to + // -|maximized_top_inset_| + |theme_image_|->height(). If this is less than + // |top_area_height_|, we need to paint the frame color to fill in the area + // beneath the image. + int theme_frame_bottom = -maximized_top_inset_ + theme_image_->height(); if (top_area_height_ > theme_frame_bottom) { canvas->FillRect(gfx::Rect(0, 0, view->width(), top_area_height_), frame_color_); } - int left_offset = 0; - int right_offset = 0; - - // Draw top-left and top-right corners to give maximized ChromeOS windows - // a rounded appearance. - if (maximized_top_left_ || maximized_top_right_) { - // If we have either a left or right we should have both. - DCHECK(maximized_top_left_ && maximized_top_right_); - left_offset = maximized_top_left_->width(); - right_offset = maximized_top_right_->width(); - canvas->DrawImageInt(*maximized_top_left_, 0, 0); - canvas->DrawImageInt(*maximized_top_right_, - view->width() - right_offset, 0); - } - // Draw the theme frame. canvas->TileImageInt(*theme_image_, - left_offset, - maximized_top_offset_, - view->width() - (left_offset + right_offset), + 0, + -maximized_top_inset_, + view->width(), theme_image_->height()); // Draw the theme frame overlay, if available. if (theme_overlay_image_) - canvas->DrawImageInt(*theme_overlay_image_, 0, theme_background_y_); + canvas->DrawImageInt(*theme_overlay_image_, 0, -maximized_top_inset_); } void FrameBackground::PaintFrameColor(gfx::Canvas* canvas, View* view) const { diff --git a/chromium/ui/views/window/frame_background.h b/chromium/ui/views/window/frame_background.h index b8684f61399..bdabf51f72e 100644 --- a/chromium/ui/views/window/frame_background.h +++ b/chromium/ui/views/window/frame_background.h @@ -44,11 +44,8 @@ class VIEWS_EXPORT FrameBackground { // which must extend behind the tab strip. void set_top_area_height(int height) { top_area_height_ = height; } - // Only used if we have an overlay image for the theme. - void set_theme_background_y(int y) { theme_background_y_ = y; } - - // Vertical offset for theme image when drawing maximized. - void set_maximized_top_offset(int offset) { maximized_top_offset_ = offset; } + // Vertical inset for theme image when drawing maximized. + void set_maximized_top_inset(int inset) { maximized_top_inset_ = inset; } // Sets images used when drawing the sides of the frame. // Caller owns the memory. @@ -94,12 +91,8 @@ class VIEWS_EXPORT FrameBackground { const gfx::ImageSkia* bottom_left_corner_; const gfx::ImageSkia* bottom_right_corner_; - // Attributes for maximized window painting. - // TODO(jamescook): Remove all these. - gfx::ImageSkia* maximized_top_left_; - gfx::ImageSkia* maximized_top_right_; - int maximized_top_offset_; - int theme_background_y_; + // Vertical inset for theme image when drawing maximized. + int maximized_top_inset_; DISALLOW_COPY_AND_ASSIGN(FrameBackground); }; diff --git a/chromium/ui/views/window/native_frame_view.cc b/chromium/ui/views/window/native_frame_view.cc index 1cbafabe57b..1ffbcdbb02b 100644 --- a/chromium/ui/views/window/native_frame_view.cc +++ b/chromium/ui/views/window/native_frame_view.cc @@ -16,6 +16,9 @@ namespace views { //////////////////////////////////////////////////////////////////////////////// // NativeFrameView, public: +// static +const char NativeFrameView::kViewClassName[] = "NativeFrameView"; + NativeFrameView::NativeFrameView(Widget* frame) : NonClientFrameView(), frame_(frame) { @@ -37,8 +40,12 @@ gfx::Rect NativeFrameView::GetWindowBoundsForClientBounds( return views::GetWindowBoundsForClientBounds( static_cast<View*>(const_cast<NativeFrameView*>(this)), client_bounds); #else - // TODO(sad): - return client_bounds; + // Enforce minimum size (1, 1) in case that |client_bounds| is passed with + // empty size. + gfx::Rect window_bounds = client_bounds; + if (window_bounds.IsEmpty()) + window_bounds.set_size(gfx::Size(1,1)); + return window_bounds; #endif } @@ -63,20 +70,30 @@ void NativeFrameView::UpdateWindowTitle() { // Nothing to do. } -// Returns the client size. On Windows, this is the expected behavior for -// native frames (see |NativeWidgetWin::WidgetSizeIsClientSize()|), while other -// platforms currently always return client bounds from -// |GetWindowBoundsForClientBounds()|. -gfx::Size NativeFrameView::GetPreferredSize() { - return frame_->client_view()->GetPreferredSize(); +gfx::Size NativeFrameView::GetPreferredSize() const { + gfx::Size client_preferred_size = frame_->client_view()->GetPreferredSize(); +#if defined(OS_WIN) + // Returns the client size. On Windows, this is the expected behavior for + // native frames (see |NativeWidgetWin::WidgetSizeIsClientSize()|), while + // other platforms currently always return client bounds from + // |GetWindowBoundsForClientBounds()|. + return client_preferred_size; +#else + return frame_->non_client_view()->GetWindowBoundsForClientBounds( + gfx::Rect(client_preferred_size)).size(); +#endif } -gfx::Size NativeFrameView::GetMinimumSize() { +gfx::Size NativeFrameView::GetMinimumSize() const { return frame_->client_view()->GetMinimumSize(); } -gfx::Size NativeFrameView::GetMaximumSize() { +gfx::Size NativeFrameView::GetMaximumSize() const { return frame_->client_view()->GetMaximumSize(); } +const char* NativeFrameView::GetClassName() const { + return kViewClassName; +} + } // namespace views diff --git a/chromium/ui/views/window/native_frame_view.h b/chromium/ui/views/window/native_frame_view.h index 378e354bf79..154453d3eba 100644 --- a/chromium/ui/views/window/native_frame_view.h +++ b/chromium/ui/views/window/native_frame_view.h @@ -13,6 +13,8 @@ class Widget; class VIEWS_EXPORT NativeFrameView : public NonClientFrameView { public: + static const char kViewClassName[]; + explicit NativeFrameView(Widget* frame); virtual ~NativeFrameView(); @@ -28,9 +30,10 @@ class VIEWS_EXPORT NativeFrameView : public NonClientFrameView { virtual void UpdateWindowTitle() OVERRIDE; // View overrides: - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual gfx::Size GetMinimumSize() OVERRIDE; - virtual gfx::Size GetMaximumSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual gfx::Size GetMinimumSize() const OVERRIDE; + virtual gfx::Size GetMaximumSize() const OVERRIDE; + virtual const char* GetClassName() const OVERRIDE; private: // Our containing frame. diff --git a/chromium/ui/views/window/non_client_view.cc b/chromium/ui/views/window/non_client_view.cc index f7cd70e5cd3..e4068dba177 100644 --- a/chromium/ui/views/window/non_client_view.cc +++ b/chromium/ui/views/window/non_client_view.cc @@ -4,7 +4,7 @@ #include "ui/views/window/non_client_view.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/base/hit_test.h" #include "ui/gfx/rect_conversions.h" #include "ui/views/rect_based_targeting_utils.h" @@ -127,14 +127,14 @@ void NonClientView::LayoutFrameView() { frame_view_->Layout(); } -void NonClientView::SetAccessibleName(const string16& name) { +void NonClientView::SetAccessibleName(const base::string16& name) { accessible_name_ = name; } //////////////////////////////////////////////////////////////////////////////// // NonClientView, View overrides: -gfx::Size NonClientView::GetPreferredSize() { +gfx::Size NonClientView::GetPreferredSize() const { // TODO(pkasting): This should probably be made to look similar to // GetMinimumSize() below. This will require implementing GetPreferredSize() // better in the various frame views. @@ -142,11 +142,11 @@ gfx::Size NonClientView::GetPreferredSize() { return GetWindowBoundsForClientBounds(client_bounds).size(); } -gfx::Size NonClientView::GetMinimumSize() { +gfx::Size NonClientView::GetMinimumSize() const { return frame_view_->GetMinimumSize(); } -gfx::Size NonClientView::GetMaximumSize() { +gfx::Size NonClientView::GetMaximumSize() const { return frame_view_->GetMaximumSize(); } @@ -177,8 +177,8 @@ void NonClientView::ViewHierarchyChanged( } } -void NonClientView::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_CLIENT; +void NonClientView::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_CLIENT; state->name = accessible_name_; } @@ -186,8 +186,8 @@ const char* NonClientView::GetClassName() const { return kViewClassName; } -views::View* NonClientView::GetEventHandlerForRect(const gfx::Rect& rect) { - if (!views::UsePointBasedTargeting(rect)) +View* NonClientView::GetEventHandlerForRect(const gfx::Rect& rect) { + if (!UsePointBasedTargeting(rect)) return View::GetEventHandlerForRect(rect); // Because of the z-ordering of our child views (the client view is positioned @@ -212,7 +212,7 @@ views::View* NonClientView::GetEventHandlerForRect(const gfx::Rect& rect) { return View::GetEventHandlerForRect(rect); } -views::View* NonClientView::GetTooltipHandlerForPoint(const gfx::Point& point) { +View* NonClientView::GetTooltipHandlerForPoint(const gfx::Point& point) { // The same logic as for |GetEventHandlerForRect()| applies here. if (frame_view_->parent() == this) { // During the reset of the frame_view_ it's possible to be in this code @@ -220,7 +220,7 @@ views::View* NonClientView::GetTooltipHandlerForPoint(const gfx::Point& point) { // removed from the NonClientView. gfx::Point point_in_child_coords(point); View::ConvertPointToTarget(this, frame_view_.get(), &point_in_child_coords); - views::View* handler = + View* handler = frame_view_->GetTooltipHandlerForPoint(point_in_child_coords); if (handler) return handler; @@ -232,12 +232,23 @@ views::View* NonClientView::GetTooltipHandlerForPoint(const gfx::Point& point) { //////////////////////////////////////////////////////////////////////////////// // NonClientFrameView, public: +NonClientFrameView::~NonClientFrameView() { +} + void NonClientFrameView::SetInactiveRenderingDisabled(bool disable) { - if (paint_as_active_ == disable) + if (inactive_rendering_disabled_ == disable) return; - paint_as_active_ = disable; - ShouldPaintAsActiveChanged(); + bool should_paint_as_active_old = ShouldPaintAsActive(); + inactive_rendering_disabled_ = disable; + + // The widget schedules a paint when the activation changes. + if (should_paint_as_active_old != ShouldPaintAsActive()) + SchedulePaint(); +} + +bool NonClientFrameView::ShouldPaintAsActive() const { + return inactive_rendering_disabled_ || GetWidget()->IsActive(); } int NonClientFrameView::GetHTComponentForFrame(const gfx::Point& point, @@ -301,16 +312,8 @@ bool NonClientFrameView::HitTestRect(const gfx::Rect& rect) const { //////////////////////////////////////////////////////////////////////////////// // NonClientFrameView, protected: -bool NonClientFrameView::ShouldPaintAsActive() const { - return GetWidget()->IsActive() || paint_as_active_; -} - -void NonClientFrameView::ShouldPaintAsActiveChanged() { - SchedulePaint(); -} - -void NonClientFrameView::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_CLIENT; +void NonClientFrameView::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_CLIENT; } const char* NonClientFrameView::GetClassName() const { @@ -322,4 +325,7 @@ void NonClientFrameView::OnBoundsChanged(const gfx::Rect& previous_bounds) { // FrameView when it is itself laid out, see comment in NonClientView::Layout. } +NonClientFrameView::NonClientFrameView() : inactive_rendering_disabled_(false) { +} + } // namespace views diff --git a/chromium/ui/views/window/non_client_view.h b/chromium/ui/views/window/non_client_view.h index 2c3b1d9fe49..77181426418 100644 --- a/chromium/ui/views/window/non_client_view.h +++ b/chromium/ui/views/window/non_client_view.h @@ -37,12 +37,18 @@ class VIEWS_EXPORT NonClientFrameView : public View { kClientEdgeThickness = 1, }; + virtual ~NonClientFrameView(); + // Sets whether the window should be rendered as active regardless of the // actual active state. Used when bubbles become active to make their parent // appear active. A value of true makes the window render as active always, // false gives normal behavior. void SetInactiveRenderingDisabled(bool disable); + // Used to determine if the frame should be painted as active. Keyed off the + // window's actual active state and |inactive_rendering_disabled_|. + bool ShouldPaintAsActive() const; + // Helper for non-client view implementations to determine which area of the // window border the specified |point| falls within. The other parameters are // the size of the sizing edges, and whether or not the window can be @@ -74,28 +80,18 @@ class VIEWS_EXPORT NonClientFrameView : public View { // Overridden from View: virtual bool HitTestRect(const gfx::Rect& rect) const OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; virtual const char* GetClassName() const OVERRIDE; protected: virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE; - NonClientFrameView() : paint_as_active_(false) {} - - // Used to determine if the frame should be painted as active. Keyed off the - // window's actual active state and the override, see - // SetInactiveRenderingDisabled() above. - bool ShouldPaintAsActive() const; - - // Invoked from SetInactiveRenderingDisabled(). This implementation invokes - // SchedulesPaint as necessary. - virtual void ShouldPaintAsActiveChanged(); + NonClientFrameView(); private: - // True when the non-client view should always be rendered as if the window - // were active, regardless of whether or not the top level window actually - // is active. - bool paint_as_active_; + // Prevents the non-client frame view from being rendered as inactive when + // true. + bool inactive_rendering_disabled_; }; //////////////////////////////////////////////////////////////////////////////// @@ -166,8 +162,6 @@ class VIEWS_EXPORT NonClientView : public View { // Prevents the window from being rendered as deactivated when |disable| is // true, until called with |disable| false. Used when a sub-window is to be // shown that shouldn't visually de-activate the window. - // Subclasses can override this to perform additional actions when this value - // changes. void SetInactiveRenderingDisabled(bool disable); // Returns the bounds of the window required to display the content area at @@ -208,14 +202,14 @@ class VIEWS_EXPORT NonClientView : public View { void LayoutFrameView(); // Set the accessible name of this view. - void SetAccessibleName(const string16& name); + void SetAccessibleName(const base::string16& name); // NonClientView, View overrides: - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual gfx::Size GetMinimumSize() OVERRIDE; - virtual gfx::Size GetMaximumSize() OVERRIDE; + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual gfx::Size GetMinimumSize() const OVERRIDE; + virtual gfx::Size GetMaximumSize() const OVERRIDE; virtual void Layout() OVERRIDE; - virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; virtual const char* GetClassName() const OVERRIDE; virtual views::View* GetEventHandlerForRect(const gfx::Rect& rect) OVERRIDE; @@ -243,7 +237,7 @@ class VIEWS_EXPORT NonClientView : public View { View* overlay_view_; // The accessible name of this view. - string16 accessible_name_; + base::string16 accessible_name_; DISALLOW_COPY_AND_ASSIGN(NonClientView); }; diff --git a/chromium/ui/views/window/window_button_order_provider.cc b/chromium/ui/views/window/window_button_order_provider.cc new file mode 100644 index 00000000000..b1ac70e4727 --- /dev/null +++ b/chromium/ui/views/window/window_button_order_provider.cc @@ -0,0 +1,41 @@ +// Copyright 2014 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/window/window_button_order_provider.h" + +namespace views { + +// static +WindowButtonOrderProvider* WindowButtonOrderProvider::instance_ = NULL; + +/////////////////////////////////////////////////////////////////////////////// +// WindowButtonOrderProvider, public: + +// static +WindowButtonOrderProvider* WindowButtonOrderProvider::GetInstance() { + if (!instance_) + instance_ = new WindowButtonOrderProvider; + return instance_; +} + +/////////////////////////////////////////////////////////////////////////////// +// WindowButtonOrderProvider, protected: + +WindowButtonOrderProvider::WindowButtonOrderProvider() { + trailing_buttons_.push_back(views::FRAME_BUTTON_MINIMIZE); + trailing_buttons_.push_back(views::FRAME_BUTTON_MAXIMIZE); + trailing_buttons_.push_back(views::FRAME_BUTTON_CLOSE); +} + +WindowButtonOrderProvider::~WindowButtonOrderProvider() { +} + +void WindowButtonOrderProvider::SetWindowButtonOrder( + const std::vector<views::FrameButton>& leading_buttons, + const std::vector<views::FrameButton>& trailing_buttons) { + leading_buttons_ = leading_buttons; + trailing_buttons_ = trailing_buttons; +} + +} // namespace views diff --git a/chromium/ui/views/window/window_button_order_provider.h b/chromium/ui/views/window/window_button_order_provider.h new file mode 100644 index 00000000000..f50a49f0fb7 --- /dev/null +++ b/chromium/ui/views/window/window_button_order_provider.h @@ -0,0 +1,56 @@ +// Copyright 2014 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_WINDOW_WINDOW_BUTTON_ORDER_PROVIDER_H_ +#define UI_VIEWS_WINDOW_WINDOW_BUTTON_ORDER_PROVIDER_H_ + +#include <vector> + +#include "base/macros.h" +#include "ui/views/views_export.h" +#include "ui/views/window/frame_buttons.h" + +namespace views { + +// Stores the ordering of window control buttons. Provides a default ordering +// of |FRAME_BUTTON_MINIMZE|, |FRAME_BUTTON_MAXIMIZE|, |FRAME_BUTTON_CLOSE|, +// where all controls are on the trailing end of a window. +// +// On Linux users can provide configuration files to control the ordering. This +// configuration is checked and overrides the defaults. +class VIEWS_EXPORT WindowButtonOrderProvider { + public: + static WindowButtonOrderProvider* GetInstance(); + + const std::vector<views::FrameButton>& leading_buttons() const { + return leading_buttons_; + } + + const std::vector<views::FrameButton>& trailing_buttons() const { + return trailing_buttons_; + } + + void SetWindowButtonOrder( + const std::vector<views::FrameButton>& leading_buttons, + const std::vector<views::FrameButton>& trailing_buttons); + + protected: + WindowButtonOrderProvider(); + virtual ~WindowButtonOrderProvider(); + + private: + static WindowButtonOrderProvider* instance_; + + // Layout arrangement of the window caption buttons. On linux these will be + // set via a WindowButtonOrderObserver. On other platforms a default + // arrangement of a trailing minimize, maximize, close, will be set. + std::vector<views::FrameButton> leading_buttons_; + std::vector<views::FrameButton> trailing_buttons_; + + DISALLOW_COPY_AND_ASSIGN(WindowButtonOrderProvider); +}; + +} // namespace views + +#endif // UI_VIEWS_WINDOW_WINDOW_BUTTON_ORDER_PROVIDER_H_ |