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/controls | |
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/controls')
182 files changed, 7893 insertions, 8764 deletions
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', + ], + }, + ], +} |