diff options
author | msw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-09 18:51:44 +0000 |
---|---|---|
committer | msw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-09 18:51:44 +0000 |
commit | ff559f2806eb11792c30fc54b544dcdb5ff8ae73 (patch) | |
tree | c160afadb093a8d5bd2926cb7ee08e06a7439ae2 /views | |
parent | b833b109f13e0d0c280f29501b2041adf419c0a9 (diff) | |
download | chromium_src-ff559f2806eb11792c30fc54b544dcdb5ff8ae73.zip chromium_src-ff559f2806eb11792c30fc54b544dcdb5ff8ae73.tar.gz chromium_src-ff559f2806eb11792c30fc54b544dcdb5ff8ae73.tar.bz2 |
Add simple drag and drop to NativeTextfieldViews.
Supports DRAG_COPY to other views/windows.
Supports DRAG_MOVE within the same view.
Fix DropHelper coordinate translation (not used elsewhere).
Fix NativeTextfieldViewsTest.DoubleAndTripleClickTest.
This patch set depends on my changes at:
http://codereview.chromium.org/6893096/
BUG=72040
TEST=--enable-textfield-views drag and drop interaction.
Review URL: http://codereview.chromium.org/6902145
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@84652 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views')
-rw-r--r-- | views/controls/textfield/native_textfield_views.cc | 112 | ||||
-rw-r--r-- | views/controls/textfield/native_textfield_views.h | 22 | ||||
-rw-r--r-- | views/controls/textfield/native_textfield_views_unittest.cc | 247 | ||||
-rw-r--r-- | views/events/event.h | 1 | ||||
-rw-r--r-- | views/widget/drop_helper.cc | 5 |
5 files changed, 380 insertions, 7 deletions
diff --git a/views/controls/textfield/native_textfield_views.cc b/views/controls/textfield/native_textfield_views.cc index 3170878..5b8e556 100644 --- a/views/controls/textfield/native_textfield_views.cc +++ b/views/controls/textfield/native_textfield_views.cc @@ -12,6 +12,7 @@ #include "base/utf_string_conversions.h" #include "grit/app_strings.h" #include "ui/base/clipboard/clipboard.h" +#include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/range/range.h" #include "ui/gfx/canvas.h" #include "ui/gfx/canvas_skia.h" @@ -66,6 +67,7 @@ NativeTextfieldViews::NativeTextfieldViews(Textfield* parent) insert_(true), is_cursor_visible_(false), skip_input_method_cancel_composition_(false), + initiating_drag_(false), ALLOW_THIS_IN_INITIALIZER_LIST(cursor_timer_(this)), aggregated_clicks_(0), last_click_time_(base::Time::FromInternalValue(0)), @@ -78,6 +80,7 @@ NativeTextfieldViews::NativeTextfieldViews(Textfield* parent) DCHECK_NE(parent->style(), Textfield::STYLE_LOWERCASE); SetContextMenuController(this); + SetDragController(this); } NativeTextfieldViews::~NativeTextfieldViews() { @@ -102,9 +105,13 @@ bool NativeTextfieldViews::OnMousePressed(const MouseEvent& event) { last_click_time_ = event.time_stamp(); last_click_location_ = event.location(); + initiating_drag_ = false; switch(aggregated_clicks_) { case 0: - MoveCursorTo(event.location(), event.IsShiftDown()); + if (!IsPointInSelection(event.location())) + MoveCursorTo(event.location(), event.IsShiftDown()); + else + initiating_drag_ = true; break; case 1: model_->SelectWord(); @@ -123,6 +130,10 @@ bool NativeTextfieldViews::OnMousePressed(const MouseEvent& event) { } bool NativeTextfieldViews::OnMouseDragged(const MouseEvent& event) { + // Don't adjust the cursor on a potential drag and drop. + if (initiating_drag_) + return true; + OnBeforeUserAction(); if (MoveCursorTo(event.location(), true)) SchedulePaint(); @@ -130,6 +141,15 @@ bool NativeTextfieldViews::OnMouseDragged(const MouseEvent& event) { return true; } +void NativeTextfieldViews::OnMouseReleased(const MouseEvent& event) { + OnBeforeUserAction(); + // Cancel suspected drag initiations, the user was clicking in the selection. + if (initiating_drag_ && MoveCursorTo(event.location(), false)) + SchedulePaint(); + initiating_drag_ = false; + OnAfterUserAction(); +} + bool NativeTextfieldViews::OnKeyPressed(const KeyEvent& event) { // OnKeyPressed/OnKeyReleased/OnFocus/OnBlur will never be invoked on // NativeTextfieldViews as it will never gain focus. @@ -142,6 +162,64 @@ bool NativeTextfieldViews::OnKeyReleased(const KeyEvent& event) { return false; } +bool NativeTextfieldViews::GetDropFormats( + int* formats, + std::set<OSExchangeData::CustomFormat>* custom_formats) { + if (!textfield_->IsEnabled() || textfield_->read_only()) + return false; + // TODO(msw): Can we support URL, FILENAME, etc.? + *formats = ui::OSExchangeData::STRING; + return true; +} + +bool NativeTextfieldViews::CanDrop(const OSExchangeData& data) { + return textfield_->IsEnabled() && !textfield_->read_only() && + data.HasString(); +} + +int NativeTextfieldViews::OnDragUpdated(const DropTargetEvent& event) { + // TODO(msw): retain unfocused selection, render secondary cursor... + DCHECK(CanDrop(event.data())); + if (initiating_drag_) { + if (IsPointInSelection(event.location())) + 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; +} + +int NativeTextfieldViews::OnPerformDrop(const DropTargetEvent& event) { + DCHECK(CanDrop(event.data())); + DCHECK(!initiating_drag_ || !IsPointInSelection(event.location())); + OnBeforeUserAction(); + size_t drop_destination = FindCursorPosition(event.location()); + // We'll delete the current selection for a drag and drop within this view. + bool move = initiating_drag_ && !event.IsControlDown() && + event.source_operations() & ui::DragDropTypes::DRAG_MOVE; + if (move) { + ui::Range selected_range; + model_->GetSelectedRange(&selected_range); + // Adjust the drop destination if it is on or after the current selection. + if (selected_range.GetMax() <= drop_destination) + drop_destination -= selected_range.length(); + else if (selected_range.GetMin() <= drop_destination) + drop_destination = selected_range.GetMin(); + model_->DeleteSelection(); + } + model_->MoveCursorTo(drop_destination, false); + string16 text; + event.data().GetString(&text); + InsertText(text); + UpdateCursorBoundsAndTextOffset(); + OnAfterUserAction(); + return move ? ui::DragDropTypes::DRAG_MOVE : ui::DragDropTypes::DRAG_COPY; +} + +void NativeTextfieldViews::OnDragDone() { + initiating_drag_ = false; +} + void NativeTextfieldViews::OnPaint(gfx::Canvas* canvas) { text_border_->set_has_focus(textfield_->HasFocus()); OnPaintBackground(canvas); @@ -159,11 +237,14 @@ void NativeTextfieldViews::OnBlur() { } gfx::NativeCursor NativeTextfieldViews::GetCursor(const MouseEvent& event) { + bool text = !initiating_drag_ && (event.type() == ui::ET_MOUSE_DRAGGED || + !IsPointInSelection(event.location())); #if defined(OS_WIN) static HCURSOR ibeam = LoadCursor(NULL, IDC_IBEAM); - return ibeam; + static HCURSOR arrow = LoadCursor(NULL, IDC_ARROW); + return text ? ibeam : arrow; #else - return gfx::GetCursor(GDK_XTERM); + return text ? gfx::GetCursor(GDK_XTERM) : NULL; #endif } @@ -177,6 +258,31 @@ void NativeTextfieldViews::ShowContextMenuForView(View* source, } ///////////////////////////////////////////////////////////////// +// 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()); +} + +int NativeTextfieldViews::GetDragOperationsForView(views::View* sender, + const gfx::Point& p) { + if (!textfield_->IsEnabled() || !IsPointInSelection(p)) + return ui::DragDropTypes::DRAG_NONE; + if (sender == this && !textfield_->read_only()) + return ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY; + return ui::DragDropTypes::DRAG_COPY; +} + +bool NativeTextfieldViews::CanStartDragForView(View* sender, + const gfx::Point& press_pt, + const gfx::Point& p) { + return IsPointInSelection(press_pt); +} + +///////////////////////////////////////////////////////////////// // NativeTextfieldViews, NativeTextifieldWrapper overrides: string16 NativeTextfieldViews::GetText() const { diff --git a/views/controls/textfield/native_textfield_views.h b/views/controls/textfield/native_textfield_views.h index 36b12c7..b20fcf1 100644 --- a/views/controls/textfield/native_textfield_views.h +++ b/views/controls/textfield/native_textfield_views.h @@ -41,6 +41,7 @@ class Menu2; // * Undo/Redo class NativeTextfieldViews : public View, public ContextMenuController, + public DragController, public NativeTextfieldWrapper, public ui::SimpleMenuModel::Delegate, public TextInputClient, @@ -53,7 +54,15 @@ class NativeTextfieldViews : public View, virtual gfx::NativeCursor GetCursor(const MouseEvent& event) OVERRIDE; virtual bool OnMousePressed(const MouseEvent& event) OVERRIDE; virtual bool OnMouseDragged(const MouseEvent& event) OVERRIDE; + virtual void OnMouseReleased(const MouseEvent& event) OVERRIDE; virtual bool OnKeyPressed(const KeyEvent& event) OVERRIDE; + virtual bool GetDropFormats( + int* formats, + std::set<OSExchangeData::CustomFormat>* custom_formats) OVERRIDE; + virtual bool CanDrop(const OSExchangeData& data) OVERRIDE; + virtual int OnDragUpdated(const DropTargetEvent& event) OVERRIDE; + virtual int OnPerformDrop(const DropTargetEvent& event) OVERRIDE; + virtual void OnDragDone() OVERRIDE; virtual bool OnKeyReleased(const KeyEvent& event) OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; virtual void OnFocus() OVERRIDE; @@ -64,6 +73,16 @@ class NativeTextfieldViews : public View, const gfx::Point& p, bool is_mouse_gesture) OVERRIDE; + // Overridden from DragController: + virtual void WriteDragDataForView(View* sender, + const gfx::Point& press_pt, + 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; @@ -230,6 +249,9 @@ class NativeTextfieldViews : public View, // 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. ScopedRunnableMethodFactory<NativeTextfieldViews> cursor_timer_; diff --git a/views/controls/textfield/native_textfield_views_unittest.cc b/views/controls/textfield/native_textfield_views_unittest.cc index cb35e79..4eb80db 100644 --- a/views/controls/textfield/native_textfield_views_unittest.cc +++ b/views/controls/textfield/native_textfield_views_unittest.cc @@ -7,10 +7,13 @@ #include "base/bind_helpers.h" #include "base/callback.h" #include "base/message_loop.h" +#include "base/pickle.h" #include "base/utf_string_conversions.h" +#include "googleurl/src/gurl.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/keycodes/keyboard_codes.h" #include "views/controls/menu/menu_2.h" #include "views/controls/textfield/native_textfield_views.h" @@ -213,6 +216,12 @@ class NativeTextfieldViewsTest : public ViewsTestBase, return widget_->GetFocusManager()->GetFocusedView(); } + int GetCursorPositionX(int cursor_pos) { + const string16 text = textfield_->text().substr(0, cursor_pos); + return textfield_view_->GetInsets().left() + textfield_view_->text_offset_ + + textfield_view_->GetFont().GetStringWidth(text); + } + // We need widget to populate wrapper class. Widget* widget_; @@ -542,22 +551,256 @@ TEST_F(NativeTextfieldViewsTest, DoubleAndTripleClickTest) { InitTextfield(Textfield::STYLE_DEFAULT); textfield_->SetText(ASCIIToUTF16("hello world")); MouseEvent click(ui::ET_MOUSE_PRESSED, 0, 0, ui::EF_LEFT_BUTTON_DOWN); + MouseEvent release(ui::ET_MOUSE_RELEASED, 0, 0, ui::EF_LEFT_BUTTON_DOWN); MouseEvent double_click(ui::ET_MOUSE_PRESSED, 0, 0, ui::EF_LEFT_BUTTON_DOWN | ui::EF_IS_DOUBLE_CLICK); // Test for double click. textfield_view_->OnMousePressed(click); - EXPECT_STR_EQ("", textfield_->GetSelectedText()); + textfield_view_->OnMouseReleased(release); + EXPECT_TRUE(textfield_->GetSelectedText().empty()); textfield_view_->OnMousePressed(double_click); + textfield_view_->OnMouseReleased(release); EXPECT_STR_EQ("hello", textfield_->GetSelectedText()); // Test for triple click. textfield_view_->OnMousePressed(click); + textfield_view_->OnMouseReleased(release); EXPECT_STR_EQ("hello world", textfield_->GetSelectedText()); // Another click should reset back to single click. textfield_view_->OnMousePressed(click); - EXPECT_STR_EQ("", textfield_->GetSelectedText()); + textfield_view_->OnMouseReleased(release); + EXPECT_TRUE(textfield_->GetSelectedText().empty()); +} + +TEST_F(NativeTextfieldViewsTest, DragToSelect) { + InitTextfield(Textfield::STYLE_DEFAULT); + textfield_->SetText(ASCIIToUTF16("hello world")); + const int kStart = GetCursorPositionX(5); + const int kEnd = 500; + MouseEvent click_a(ui::ET_MOUSE_PRESSED, kStart, 0, ui::EF_LEFT_BUTTON_DOWN); + MouseEvent click_b(ui::ET_MOUSE_PRESSED, kEnd, 0, ui::EF_LEFT_BUTTON_DOWN); + MouseEvent drag_left(ui::ET_MOUSE_DRAGGED, 0, 0, ui::EF_LEFT_BUTTON_DOWN); + MouseEvent drag_right(ui::ET_MOUSE_DRAGGED, kEnd, 0, ui::EF_LEFT_BUTTON_DOWN); + MouseEvent release(ui::ET_MOUSE_RELEASED, kEnd, 0, ui::EF_LEFT_BUTTON_DOWN); + textfield_view_->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(); + 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(); + EXPECT_STR_EQ(" world", text_right); + // Check that releasing in the same location does not alter the selection. + textfield_view_->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); + EXPECT_EQ(textfield_->text(), textfield_->GetSelectedText()); +} + +TEST_F(NativeTextfieldViewsTest, DragAndDrop_AcceptDrop) { + InitTextfield(Textfield::STYLE_DEFAULT); + textfield_->SetText(ASCIIToUTF16("hello world")); + + ui::OSExchangeData data; + 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_EQ(0, formats); + EXPECT_TRUE(custom_formats.empty()); + EXPECT_FALSE(textfield_view_->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_EQ(0, formats); + EXPECT_TRUE(custom_formats.empty()); + EXPECT_FALSE(textfield_view_->CanDrop(data)); + textfield_->SetReadOnly(false); + + // Ensure that enabled and editable textfields do accept drops. + EXPECT_TRUE(textfield_view_->GetDropFormats(&formats, &custom_formats)); + EXPECT_EQ(ui::OSExchangeData::STRING, formats); + EXPECT_TRUE(custom_formats.empty()); + EXPECT_TRUE(textfield_view_->CanDrop(data)); + DropTargetEvent drop(data, GetCursorPositionX(6), 0, + 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)); + 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(FilePath(FILE_PATH_LITERAL("x"))); +#if defined(OS_WIN) + bad_data.SetPickledData(CF_BITMAP, Pickle()); + bad_data.SetFileContents(FilePath(L"x"), "x"); + bad_data.SetHtml(string16(ASCIIToUTF16("x")), GURL("x.org")); + ui::OSExchangeData::DownloadFileInfo download(FilePath(), NULL); + bad_data.SetDownloadFileInfo(download); +#else + // Skip OSExchangeDataProviderWin::SetURL, which also sets CF_TEXT / STRING. + bad_data.SetURL(GURL("x.org"), string16(ASCIIToUTF16("x"))); + bad_data.SetPickledData(GDK_SELECTION_PRIMARY, Pickle()); +#endif + EXPECT_FALSE(textfield_view_->CanDrop(bad_data)); +} + +TEST_F(NativeTextfieldViewsTest, DragAndDrop_InitiateDrag) { + InitTextfield(Textfield::STYLE_DEFAULT); + textfield_->SetText(ASCIIToUTF16("hello string world")); + + // Ensure the textfield will provide selected text for drag data. + string16 string; + ui::OSExchangeData data; + const ui::Range kStringRange(6, 12); + textfield_->SelectRange(kStringRange); + const gfx::Point kStringPoint(GetCursorPositionX(9), 0); + textfield_view_->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_->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_->SelectRange(kStringRange); + // Ensure that textfields only initiate drag operations inside the selection. + EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, + textfield_view_->GetDragOperationsForView(NULL, gfx::Point())); + EXPECT_FALSE(textfield_view_->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())); + // Ensure that textfields support local moves. + EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY, + textfield_view_->GetDragOperationsForView(textfield_view_, kStringPoint)); +} + +TEST_F(NativeTextfieldViewsTest, DragAndDrop_ToTheRight) { + InitTextfield(Textfield::STYLE_DEFAULT); + textfield_->SetText(ASCIIToUTF16("hello world")); + + string16 string; + ui::OSExchangeData data; + int formats = 0; + int operations = 0; + std::set<OSExchangeData::CustomFormat> custom_formats; + + // Start dragging "ello". + textfield_->SelectRange(ui::Range(1, 5)); + MouseEvent click_a(ui::ET_MOUSE_PRESSED, GetCursorPositionX(3), 0, + ui::EF_LEFT_BUTTON_DOWN); + 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()); + EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY, + operations); + textfield_view_->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_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)); + DropTargetEvent drop_a(data, kDropPoint.x(), 0, operations); + EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE, + textfield_view_->OnDragUpdated(drop_a)); + EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE, + textfield_view_->OnPerformDrop(drop_a)); + EXPECT_STR_EQ("h welloorld", textfield_->text()); + textfield_view_->OnDragDone(); +} + +TEST_F(NativeTextfieldViewsTest, DragAndDrop_ToTheLeft) { + InitTextfield(Textfield::STYLE_DEFAULT); + textfield_->SetText(ASCIIToUTF16("hello world")); + + string16 string; + ui::OSExchangeData data; + int formats = 0; + int operations = 0; + std::set<OSExchangeData::CustomFormat> custom_formats; + + // Start dragging " worl". + textfield_->SelectRange(ui::Range(5, 10)); + MouseEvent click_a(ui::ET_MOUSE_PRESSED, GetCursorPositionX(7), 0, + ui::EF_LEFT_BUTTON_DOWN); + 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()); + EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY, + operations); + textfield_view_->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_EQ(ui::OSExchangeData::STRING, formats); + EXPECT_TRUE(custom_formats.empty()); + + // Drop " worl" after "h". + EXPECT_TRUE(textfield_view_->CanDrop(data)); + DropTargetEvent drop_a(data, GetCursorPositionX(1), 0, operations); + EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE, + textfield_view_->OnDragUpdated(drop_a)); + EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE, + textfield_view_->OnPerformDrop(drop_a)); + EXPECT_STR_EQ("h worlellod", textfield_->text()); + textfield_view_->OnDragDone(); +} + +TEST_F(NativeTextfieldViewsTest, DragAndDrop_Canceled) { + InitTextfield(Textfield::STYLE_DEFAULT); + textfield_->SetText(ASCIIToUTF16("hello world")); + + // Start dragging "worl". + textfield_->SelectRange(ui::Range(6, 10)); + MouseEvent click(ui::ET_MOUSE_PRESSED, GetCursorPositionX(8), 0, + ui::EF_LEFT_BUTTON_DOWN); + textfield_view_->OnMousePressed(click); + ui::OSExchangeData data; + textfield_view_->WriteDragDataForView(NULL, click.location(), &data); + EXPECT_TRUE(textfield_view_->CanDrop(data)); + // Drag the text over somewhere valid, outside the current selection. + DropTargetEvent drop(data, GetCursorPositionX(2), 0, + ui::DragDropTypes::DRAG_MOVE); + EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE, textfield_view_->OnDragUpdated(drop)); + // "Cancel" the drag, via move and release over the selection, and OnDragDone. + MouseEvent drag(ui::ET_MOUSE_DRAGGED, GetCursorPositionX(9), 0, + ui::EF_LEFT_BUTTON_DOWN); + MouseEvent release(ui::ET_MOUSE_RELEASED, GetCursorPositionX(9), 0, + ui::EF_LEFT_BUTTON_DOWN); + textfield_view_->OnMouseDragged(drag); + textfield_view_->OnMouseReleased(release); + textfield_view_->OnDragDone(); + EXPECT_EQ(ASCIIToUTF16("hello world"), textfield_->text()); } TEST_F(NativeTextfieldViewsTest, ReadOnlyTest) { diff --git a/views/events/event.h b/views/events/event.h index fbe3556..88b4693 100644 --- a/views/events/event.h +++ b/views/events/event.h @@ -382,6 +382,7 @@ class DropTargetEvent : public LocatedEvent { : LocatedEvent(ui::ET_DROP_TARGET_EVENT, gfx::Point(x, y), 0), data_(data), source_operations_(source_operations) { + // TODO(msw): Hook up key state flags for CTRL + drag and drop, etc. } const OSExchangeData& data() const { return data_; } diff --git a/views/widget/drop_helper.cc b/views/widget/drop_helper.cc index 4090a6b..a0b7b76 100644 --- a/views/widget/drop_helper.cc +++ b/views/widget/drop_helper.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// 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. @@ -61,7 +61,8 @@ int DropHelper::OnDrop(const OSExchangeData& data, } gfx::Point view_location(root_view_location); - View::ConvertPointToView(NULL, drop_view, &view_location); + View* root_view = drop_view->GetRootView(); + View::ConvertPointToView(root_view, drop_view, &view_location); DropTargetEvent drop_event(data, view_location.x(), view_location.y(), drag_operation); return drop_view->OnPerformDrop(drop_event); |