Commit cf5eeb5c authored by spqchan's avatar spqchan Committed by Commit bot

MacViews: Implemented Drag & Drop

Created a Drag & Drop client on Mac that bridges between the Views
and native OSX's drag and drop. This approach is based on the Aura
approach.

BUG=599585
TEST= Run the view unit test: DragDropClientMacTest

Review-Url: https://codereview.chromium.org/1964283002
Cr-Commit-Position: refs/heads/master@{#397801}
parent d5c75af8
...@@ -243,6 +243,13 @@ typedef NSInteger NSPressureBehavior; ...@@ -243,6 +243,13 @@ typedef NSInteger NSPressureBehavior;
- (instancetype)initWithPressureBehavior:(NSPressureBehavior)pressureBehavior; - (instancetype)initWithPressureBehavior:(NSPressureBehavior)pressureBehavior;
@end @end
enum {
NSSpringLoadingHighlightNone = 0,
NSSpringLoadingHighlightStandard,
NSSpringLoadingHighlightEmphasized
};
typedef NSUInteger NSSpringLoadingHighlight;
#endif // MAC_OS_X_VERSION_10_11 #endif // MAC_OS_X_VERSION_10_11
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
......
...@@ -151,11 +151,10 @@ component("base") { ...@@ -151,11 +151,10 @@ component("base") {
"device_form_factor_desktop.cc", "device_form_factor_desktop.cc",
"device_form_factor_ios.mm", "device_form_factor_ios.mm",
"dragdrop/drag_drop_types.h", "dragdrop/drag_drop_types.h",
"dragdrop/drag_drop_types_mac.mm",
"dragdrop/drag_drop_types_win.cc", "dragdrop/drag_drop_types_win.cc",
"dragdrop/drag_source_win.cc", "dragdrop/drag_source_win.cc",
"dragdrop/drag_source_win.h", "dragdrop/drag_source_win.h",
"dragdrop/drag_utils_aura.cc",
"dragdrop/drag_utils_mac.mm",
"dragdrop/drag_utils_win.cc", "dragdrop/drag_utils_win.cc",
"dragdrop/drop_target_event.cc", "dragdrop/drop_target_event.cc",
"dragdrop/drop_target_event.h", "dragdrop/drop_target_event.h",
...@@ -440,7 +439,6 @@ component("base") { ...@@ -440,7 +439,6 @@ component("base") {
sources -= [ sources -= [
"cursor/cursor.cc", "cursor/cursor.cc",
"cursor/cursor.h", "cursor/cursor.h",
"dragdrop/drag_utils_aura.cc",
] ]
} }
...@@ -448,10 +446,6 @@ component("base") { ...@@ -448,10 +446,6 @@ component("base") {
sources -= [ "resource/resource_bundle_auralinux.cc" ] sources -= [ "resource/resource_bundle_auralinux.cc" ]
} }
if (use_aura && is_win) {
sources -= [ "dragdrop/drag_utils_aura.cc" ]
}
if (is_linux) { if (is_linux) {
deps += [ "//build/linux:fontconfig" ] deps += [ "//build/linux:fontconfig" ]
} }
...@@ -519,8 +513,6 @@ component("base") { ...@@ -519,8 +513,6 @@ component("base") {
sources -= [ sources -= [
"cursor/image_cursors.cc", "cursor/image_cursors.cc",
"cursor/image_cursors.h", "cursor/image_cursors.h",
"dragdrop/drag_utils.cc",
"dragdrop/drag_utils.h",
] ]
libs += [ libs += [
......
...@@ -32,6 +32,10 @@ class UI_BASE_EXPORT DragDropTypes { ...@@ -32,6 +32,10 @@ class UI_BASE_EXPORT DragDropTypes {
static uint32_t DragOperationToDropEffect(int drag_operation); static uint32_t DragOperationToDropEffect(int drag_operation);
static int DropEffectToDragOperation(uint32_t effect); static int DropEffectToDragOperation(uint32_t effect);
#endif #endif
#if defined(OS_MACOSX)
static uint64_t DragOperationToNSDragOperation(int drag_operation);
#endif
}; };
} // namespace ui } // namespace ui
......
// Copyright 2016 The Chromium 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/base/dragdrop/drag_drop_types.h"
#import <Cocoa/Cocoa.h>
namespace ui {
uint64_t ui::DragDropTypes::DragOperationToNSDragOperation(int drag_operation) {
NSUInteger ns_drag_operation = NSDragOperationNone;
if (drag_operation & DRAG_LINK)
ns_drag_operation |= NSDragOperationLink;
if (drag_operation & DRAG_COPY)
ns_drag_operation |= NSDragOperationCopy;
if (drag_operation & DRAG_MOVE)
ns_drag_operation |= NSDragOperationMove;
return ns_drag_operation;
}
} // namespace ui
...@@ -103,4 +103,12 @@ void SetDragImageOnDataObject(const gfx::Canvas& canvas, ...@@ -103,4 +103,12 @@ void SetDragImageOnDataObject(const gfx::Canvas& canvas,
SetDragImageOnDataObject(image, cursor_offset, data_object); SetDragImageOnDataObject(image, cursor_offset, data_object);
} }
#if !defined(OS_WIN)
void SetDragImageOnDataObject(const gfx::ImageSkia& image_skia,
const gfx::Vector2d& cursor_offset,
ui::OSExchangeData* data_object) {
data_object->provider().SetDragImage(image_skia, cursor_offset);
}
#endif
} // namespace drag_utils } // namespace drag_utils
// 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/base/dragdrop/drag_utils.h"
#include "base/logging.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/image/image_skia.h"
namespace drag_utils {
void SetDragImageOnDataObject(const gfx::ImageSkia& image,
const gfx::Vector2d& cursor_offset,
ui::OSExchangeData* data_object) {
data_object->provider().SetDragImage(image, cursor_offset);
}
} // namespace drag_utils
// 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/base/dragdrop/drag_utils.h"
#include "base/logging.h"
namespace drag_utils {
void SetDragImageOnDataObject(const gfx::Canvas& canvas,
const gfx::Vector2d& cursor_offset,
ui::OSExchangeData* data_object) {
NOTIMPLEMENTED();
}
void SetDragImageOnDataObject(const gfx::ImageSkia& image_skia,
const gfx::Vector2d& cursor_offset,
ui::OSExchangeData* data_object) {
NOTIMPLEMENTED();
}
} // namespace drag_utils
...@@ -130,7 +130,7 @@ class UI_BASE_EXPORT OSExchangeData { ...@@ -130,7 +130,7 @@ class UI_BASE_EXPORT OSExchangeData {
virtual bool HasHtml() const = 0; virtual bool HasHtml() const = 0;
#endif #endif
#if defined(USE_AURA) #if defined(USE_AURA) || defined(OS_MACOSX)
virtual void SetDragImage(const gfx::ImageSkia& image, virtual void SetDragImage(const gfx::ImageSkia& image,
const gfx::Vector2d& cursor_offset) = 0; const gfx::Vector2d& cursor_offset) = 0;
virtual const gfx::ImageSkia& GetDragImage() const = 0; virtual const gfx::ImageSkia& GetDragImage() const = 0;
......
...@@ -9,8 +9,13 @@ ...@@ -9,8 +9,13 @@
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#import "ui/base/clipboard/clipboard_util_mac.h" #import "ui/base/clipboard/clipboard_util_mac.h"
#include "ui/base/dragdrop/os_exchange_data.h" #include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/image/image_skia.h"
@class NSArray;
@class NSData;
@class NSPasteboard; @class NSPasteboard;
@class NSString;
namespace ui { namespace ui {
class UniquePasteboard; class UniquePasteboard;
...@@ -44,11 +49,30 @@ class UI_BASE_EXPORT OSExchangeDataProviderMac ...@@ -44,11 +49,30 @@ class UI_BASE_EXPORT OSExchangeDataProviderMac
bool HasURL(OSExchangeData::FilenameToURLPolicy policy) const override; bool HasURL(OSExchangeData::FilenameToURLPolicy policy) const override;
bool HasFile() const override; bool HasFile() const override;
bool HasCustomFormat(const Clipboard::FormatType& format) const override; bool HasCustomFormat(const Clipboard::FormatType& format) const override;
void SetDragImage(const gfx::ImageSkia& image,
const gfx::Vector2d& cursor_offset) override;
const gfx::ImageSkia& GetDragImage() const override;
const gfx::Vector2d& GetDragImageOffset() const override;
// Returns the data for the specified type in the pasteboard.
NSData* GetNSDataForType(NSString* type) const;
// Creates an OSExchangeData object from the given NSPasteboard object.
static std::unique_ptr<OSExchangeData> CreateDataFromPasteboard(
NSPasteboard* pasteboard);
// Returns an array of pasteboard types that can be supported by
// OSExchangeData.
static NSArray* SupportedPasteboardTypes();
private: private:
explicit OSExchangeDataProviderMac(scoped_refptr<ui::UniquePasteboard>); explicit OSExchangeDataProviderMac(scoped_refptr<ui::UniquePasteboard>);
scoped_refptr<ui::UniquePasteboard> pasteboard_; scoped_refptr<ui::UniquePasteboard> pasteboard_;
// Drag image and offset data.
gfx::ImageSkia drag_image_;
gfx::Vector2d cursor_offset_;
DISALLOW_COPY_AND_ASSIGN(OSExchangeDataProviderMac); DISALLOW_COPY_AND_ASSIGN(OSExchangeDataProviderMac);
}; };
......
...@@ -7,11 +7,14 @@ ...@@ -7,11 +7,14 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/pickle.h" #include "base/pickle.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#import "third_party/mozilla/NSPasteboard+Utils.h" #import "third_party/mozilla/NSPasteboard+Utils.h"
#import "ui/base/clipboard/clipboard_util_mac.h" #import "ui/base/clipboard/clipboard_util_mac.h"
#include "ui/base/clipboard/custom_data_helper.h"
#import "ui/base/dragdrop/cocoa_dnd_util.h"
#include "url/gurl.h" #include "url/gurl.h"
namespace ui { namespace ui {
...@@ -169,6 +172,46 @@ bool OSExchangeDataProviderMac::HasCustomFormat( ...@@ -169,6 +172,46 @@ bool OSExchangeDataProviderMac::HasCustomFormat(
return [[pasteboard_->get() types] containsObject:format.ToNSString()]; return [[pasteboard_->get() types] containsObject:format.ToNSString()];
} }
void OSExchangeDataProviderMac::SetDragImage(
const gfx::ImageSkia& image,
const gfx::Vector2d& cursor_offset) {
drag_image_ = image;
cursor_offset_ = cursor_offset;
}
const gfx::ImageSkia& OSExchangeDataProviderMac::GetDragImage() const {
return drag_image_;
}
const gfx::Vector2d& OSExchangeDataProviderMac::GetDragImageOffset() const {
return cursor_offset_;
}
NSData* OSExchangeDataProviderMac::GetNSDataForType(NSString* type) const {
return [pasteboard_->get() dataForType:type];
}
// static
std::unique_ptr<OSExchangeData>
OSExchangeDataProviderMac::CreateDataFromPasteboard(NSPasteboard* pasteboard) {
OSExchangeDataProviderMac* provider = new OSExchangeDataProviderMac();
for (NSPasteboardItem* item in [pasteboard pasteboardItems])
ClipboardUtil::AddDataToPasteboard(provider->pasteboard_->get(), item);
return base::MakeUnique<OSExchangeData>(provider);
}
// static
NSArray* OSExchangeDataProviderMac::SupportedPasteboardTypes() {
return @[
kWebCustomDataPboardType, ui::ClipboardUtil::UTIForWebURLsAndTitles(),
NSURLPboardType, NSFilenamesPboardType, ui::kChromeDragDummyPboardType,
NSStringPboardType, NSHTMLPboardType, NSRTFPboardType,
NSFilenamesPboardType, ui::kWebCustomDataPboardType, NSPasteboardTypeString
];
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// OSExchangeData, public: // OSExchangeData, public:
......
...@@ -222,13 +222,12 @@ ...@@ -222,13 +222,12 @@
'dragdrop/cocoa_dnd_util.h', 'dragdrop/cocoa_dnd_util.h',
'dragdrop/cocoa_dnd_util.mm', 'dragdrop/cocoa_dnd_util.mm',
'dragdrop/drag_drop_types.h', 'dragdrop/drag_drop_types.h',
'dragdrop/drag_drop_types_mac.mm',
'dragdrop/drag_drop_types_win.cc', 'dragdrop/drag_drop_types_win.cc',
'dragdrop/drag_source_win.cc', 'dragdrop/drag_source_win.cc',
'dragdrop/drag_source_win.h', 'dragdrop/drag_source_win.h',
'dragdrop/drag_utils.cc', 'dragdrop/drag_utils.cc',
'dragdrop/drag_utils.h', 'dragdrop/drag_utils.h',
'dragdrop/drag_utils_aura.cc',
'dragdrop/drag_utils_mac.mm',
'dragdrop/drag_utils_win.cc', 'dragdrop/drag_utils_win.cc',
'dragdrop/drop_target_event.cc', 'dragdrop/drop_target_event.cc',
'dragdrop/drop_target_event.h', 'dragdrop/drop_target_event.h',
...@@ -462,11 +461,6 @@ ...@@ -462,11 +461,6 @@
'../ozone/ozone.gyp:ozone_base', '../ozone/ozone.gyp:ozone_base',
], ],
}], }],
['use_aura==1 and OS=="win"', {
'sources/': [
['exclude', 'dragdrop/drag_utils_aura.cc'],
],
}],
['use_glib == 1', { ['use_glib == 1', {
'dependencies': [ 'dependencies': [
'../../build/linux/system.gyp:fontconfig', '../../build/linux/system.gyp:fontconfig',
...@@ -575,8 +569,6 @@ ...@@ -575,8 +569,6 @@
'sources!': [ 'sources!': [
'cursor/image_cursors.cc', 'cursor/image_cursors.cc',
'cursor/image_cursors.h', 'cursor/image_cursors.h',
'dragdrop/drag_utils.cc',
'dragdrop/drag_utils.h',
], ],
'link_settings': { 'link_settings': {
'libraries': [ 'libraries': [
......
...@@ -22,8 +22,9 @@ class View; ...@@ -22,8 +22,9 @@ class View;
// The NSView that sits as the root contentView of the NSWindow, whilst it has // The NSView that sits as the root contentView of the NSWindow, whilst it has
// a views::RootView present. Bridges requests from Cocoa to the hosted // a views::RootView present. Bridges requests from Cocoa to the hosted
// views::View. // views::View.
@interface BridgedContentView @interface BridgedContentView : ToolTipBaseView<NSTextInputClient,
: ToolTipBaseView<NSTextInputClient, NSUserInterfaceValidations> { NSUserInterfaceValidations,
NSDraggingSource> {
@private @private
// Weak. The hosted RootView, owned by hostedView_->GetWidget(). // Weak. The hosted RootView, owned by hostedView_->GetWidget().
views::View* hostedView_; views::View* hostedView_;
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "skia/ext/skia_utils_mac.h" #include "skia/ext/skia_utils_mac.h"
#include "ui/base/cocoa/cocoa_base_utils.h" #include "ui/base/cocoa/cocoa_base_utils.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/os_exchange_data_provider_mac.h"
#include "ui/base/ime/input_method.h" #include "ui/base/ime/input_method.h"
#include "ui/base/ime/text_input_client.h" #include "ui/base/ime/text_input_client.h"
#include "ui/compositor/canvas_painter.h" #include "ui/compositor/canvas_painter.h"
...@@ -23,9 +25,12 @@ ...@@ -23,9 +25,12 @@
#import "ui/gfx/path_mac.h" #import "ui/gfx/path_mac.h"
#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
#include "ui/strings/grit/ui_strings.h" #include "ui/strings/grit/ui_strings.h"
#import "ui/views/cocoa/bridged_native_widget.h"
#import "ui/views/cocoa/drag_drop_client_mac.h"
#include "ui/views/controls/menu/menu_config.h" #include "ui/views/controls/menu/menu_config.h"
#include "ui/views/controls/menu/menu_controller.h" #include "ui/views/controls/menu/menu_controller.h"
#include "ui/views/view.h" #include "ui/views/view.h"
#include "ui/views/widget/native_widget_mac.h"
#include "ui/views/widget/widget.h" #include "ui/views/widget/widget.h"
using views::MenuController; using views::MenuController;
...@@ -225,6 +230,9 @@ base::string16 AttributedSubstringForRangeHelper( ...@@ -225,6 +230,9 @@ base::string16 AttributedSubstringForRangeHelper(
// Notification handler invoked when the Full Keyboard Access mode is changed. // Notification handler invoked when the Full Keyboard Access mode is changed.
- (void)onFullKeyboardAccessModeChanged:(NSNotification*)notification; - (void)onFullKeyboardAccessModeChanged:(NSNotification*)notification;
// Returns the native Widget's drag drop client. Possibly null.
- (views::DragDropClientMac*)dragDropClient;
// Menu action handlers. // Menu action handlers.
- (void)undo:(id)sender; - (void)undo:(id)sender;
- (void)redo:(id)sender; - (void)redo:(id)sender;
...@@ -272,6 +280,8 @@ base::string16 AttributedSubstringForRangeHelper( ...@@ -272,6 +280,8 @@ base::string16 AttributedSubstringForRangeHelper(
// Initialize the focus manager with the correct keyboard accessibility // Initialize the focus manager with the correct keyboard accessibility
// setting. // setting.
[self updateFullKeyboardAccess]; [self updateFullKeyboardAccess];
[self registerForDraggedTypes:ui::OSExchangeDataProviderMac::
SupportedPasteboardTypes()];
} }
return self; return self;
} }
...@@ -396,6 +406,12 @@ base::string16 AttributedSubstringForRangeHelper( ...@@ -396,6 +406,12 @@ base::string16 AttributedSubstringForRangeHelper(
[self updateFullKeyboardAccess]; [self updateFullKeyboardAccess];
} }
- (views::DragDropClientMac*)dragDropClient {
views::BridgedNativeWidget* bridge =
views::NativeWidgetMac::GetBridgeForNativeWindow([self window]);
return bridge ? bridge->drag_drop_client() : nullptr;
}
- (void)undo:(id)sender { - (void)undo:(id)sender {
// This DCHECK is more strict than a similar check in handleAction:. It can be // This DCHECK is more strict than a similar check in handleAction:. It can be
// done here because the actors sending these actions should be calling // done here because the actors sending these actions should be calling
...@@ -585,6 +601,22 @@ base::string16 AttributedSubstringForRangeHelper( ...@@ -585,6 +601,22 @@ base::string16 AttributedSubstringForRangeHelper(
return YES; return YES;
} }
// NSDraggingDestination protocol overrides.
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
return [self draggingUpdated:sender];
}
- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
views::DragDropClientMac* client = [self dragDropClient];
return client ? client->DragUpdate(sender) : ui::DragDropTypes::DRAG_NONE;
}
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
views::DragDropClientMac* client = [self dragDropClient];
return client && client->Drop(sender) != NSDragOperationNone;
}
- (NSTextInputContext*)inputContext { - (NSTextInputContext*)inputContext {
// If the textInputClient_ does not exist, return nil since this view does not // If the textInputClient_ does not exist, return nil since this view does not
// conform to NSTextInputClient protocol. // conform to NSTextInputClient protocol.
...@@ -1177,6 +1209,21 @@ base::string16 AttributedSubstringForRangeHelper( ...@@ -1177,6 +1209,21 @@ base::string16 AttributedSubstringForRangeHelper(
return NO; return NO;
} }
// NSDraggingSource protocol implementation.
- (NSDragOperation)draggingSession:(NSDraggingSession*)session
sourceOperationMaskForDraggingContext:(NSDraggingContext)context {
return NSDragOperationEvery;
}
- (void)draggingSession:(NSDraggingSession*)session
endedAtPoint:(NSPoint)screenPoint
operation:(NSDragOperation)operation {
views::DragDropClientMac* client = [self dragDropClient];
if (client)
client->EndDrag();
}
// NSAccessibility informal protocol implementation. // NSAccessibility informal protocol implementation.
- (id)accessibilityAttributeValue:(NSString*)attribute { - (id)accessibilityAttributeValue:(NSString*)attribute {
......
...@@ -34,6 +34,7 @@ class BridgedNativeWidgetTestApi; ...@@ -34,6 +34,7 @@ class BridgedNativeWidgetTestApi;
} }
class CocoaMouseCapture; class CocoaMouseCapture;
class DragDropClientMac;
class NativeWidgetMac; class NativeWidgetMac;
class View; class View;
...@@ -171,6 +172,8 @@ class VIEWS_EXPORT BridgedNativeWidget ...@@ -171,6 +172,8 @@ class VIEWS_EXPORT BridgedNativeWidget
TooltipManager* tooltip_manager() { return tooltip_manager_.get(); } TooltipManager* tooltip_manager() { return tooltip_manager_.get(); }
DragDropClientMac* drag_drop_client() { return drag_drop_client_.get(); }
// The parent widget specified in Widget::InitParams::parent. If non-null, the // The parent widget specified in Widget::InitParams::parent. If non-null, the
// parent will close children before the parent closes, and children will be // parent will close children before the parent closes, and children will be
// raised above their parent when window z-order changes. // raised above their parent when window z-order changes.
...@@ -263,6 +266,7 @@ class VIEWS_EXPORT BridgedNativeWidget ...@@ -263,6 +266,7 @@ class VIEWS_EXPORT BridgedNativeWidget
std::unique_ptr<ui::InputMethod> input_method_; std::unique_ptr<ui::InputMethod> input_method_;
std::unique_ptr<CocoaMouseCapture> mouse_capture_; std::unique_ptr<CocoaMouseCapture> mouse_capture_;
std::unique_ptr<TooltipManager> tooltip_manager_; std::unique_ptr<TooltipManager> tooltip_manager_;
std::unique_ptr<DragDropClientMac> drag_drop_client_;
FocusManager* focus_manager_; // Weak. Owned by our Widget. FocusManager* focus_manager_; // Weak. Owned by our Widget.
Widget::InitParams::Type widget_type_; Widget::InitParams::Type widget_type_;
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#import "ui/gfx/mac/coordinate_conversion.h" #import "ui/gfx/mac/coordinate_conversion.h"
#import "ui/gfx/mac/nswindow_frame_controls.h" #import "ui/gfx/mac/nswindow_frame_controls.h"
#import "ui/views/cocoa/bridged_content_view.h" #import "ui/views/cocoa/bridged_content_view.h"
#import "ui/views/cocoa/drag_drop_client_mac.h"
#import "ui/views/cocoa/cocoa_mouse_capture.h" #import "ui/views/cocoa/cocoa_mouse_capture.h"
#include "ui/views/cocoa/tooltip_manager_mac.h" #include "ui/views/cocoa/tooltip_manager_mac.h"
#import "ui/views/cocoa/views_nswindow_delegate.h" #import "ui/views/cocoa/views_nswindow_delegate.h"
...@@ -519,6 +520,7 @@ void BridgedNativeWidget::SetRootView(views::View* view) { ...@@ -519,6 +520,7 @@ void BridgedNativeWidget::SetRootView(views::View* view) {
// and replaced, pointing at the new view. // and replaced, pointing at the new view.
DCHECK(!view || !compositor_widget_); DCHECK(!view || !compositor_widget_);
drag_drop_client_.reset();
[bridged_view_ clearView]; [bridged_view_ clearView];
bridged_view_.reset(); bridged_view_.reset();
// Note that there can still be references to the old |bridged_view_| // Note that there can still be references to the old |bridged_view_|
...@@ -527,6 +529,8 @@ void BridgedNativeWidget::SetRootView(views::View* view) { ...@@ -527,6 +529,8 @@ void BridgedNativeWidget::SetRootView(views::View* view) {
if (view) { if (view) {
bridged_view_.reset([[BridgedContentView alloc] initWithView:view]); bridged_view_.reset([[BridgedContentView alloc] initWithView:view]);
drag_drop_client_.reset(new DragDropClientMac(this, view));
// Objective C initializers can return nil. However, if |view| is non-NULL // Objective C initializers can return nil. However, if |view| is non-NULL
// this should be treated as an error and caught early. // this should be treated as an error and caught early.
CHECK(bridged_view_); CHECK(bridged_view_);
......
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_VIEWS_COCOA_DRAG_DROP_CLIENT_MAC_H_
#define UI_VIEWS_COCOA_DRAG_DROP_CLIENT_MAC_H_
#import <Cocoa/Cocoa.h>
#include "base/callback.h"
#import "base/mac/scoped_nsobject.h"
#include "base/macros.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/views/views_export.h"
#include "ui/views/widget/drop_helper.h"
// This class acts as a bridge between NSPasteboardItem and OSExchangeData by
// implementing NSPasteboardItemDataProvider and writing data from
// OSExchangeData into the pasteboard.
VIEWS_EXPORT
@interface CocoaDragDropDataProvider : NSObject<NSPasteboardItemDataProvider>
- (id)initWithData:(const ui::OSExchangeData&)data;
@end
namespace gfx {
class Point;
}
namespace views {
namespace test {
class DragDropClientMacTest;
}
class BridgedNativeWidget;
class View;
// Implements drag and drop on MacViews. This class acts as a bridge between
// the Views and native system's drag and drop. This class mimics
// DesktopDragDropClientAuraX11.
class VIEWS_EXPORT DragDropClientMac {
public:
explicit DragDropClientMac(BridgedNativeWidget* bridge, View* root_view);
~DragDropClientMac();
// Initiates a drag and drop session. Returns the drag operation that was
// applied at the end of the drag drop session.
void StartDragAndDrop(View* view,
const ui::OSExchangeData& data,
int operation,
ui::DragDropTypes::DragEventSource source);
// Called when mouse is dragged during a drag and drop.
NSDragOperation DragUpdate(id<NSDraggingInfo>);
// Called when mouse is released during a drag and drop.
NSDragOperation Drop(id<NSDraggingInfo> sender);
// Called when the drag and drop session has ended.
void EndDrag();
DropHelper* drop_helper() { return &drop_helper_; }
private:
friend class test::DragDropClientMacTest;
// Converts the given NSPoint to the coordinate system in Views.
gfx::Point LocationInView(NSPoint point) const;
// Provides the data for the drag and drop session.
base::scoped_nsobject<CocoaDragDropDataProvider> data_source_;
// Used to handle drag and drop with Views.
DropHelper drop_helper_;
// The drag and drop operation.
int operation_;
// The bridge between the content view and the drag drop client.
BridgedNativeWidget* bridge_; // Weak. Owns |this|.
// The closure for the drag and drop's run loop.
base::Closure quit_closure_;
DISALLOW_COPY_AND_ASSIGN(DragDropClientMac);
};
} // namespace views
#endif // UI_VIEWS_COCOA_DRAG_DROP_CLIENT_MAC_H_
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ui/views/cocoa/drag_drop_client_mac.h"
#include "base/mac/mac_util.h"
#include "base/run_loop.h"
#include "base/strings/sys_string_conversions.h"
#import "ui/base/dragdrop/os_exchange_data_provider_mac.h"
#include "ui/gfx/image/image_skia_util_mac.h"
#include "ui/views/drag_utils.h"
#import "ui/views/cocoa/bridged_content_view.h"
#import "ui/views/cocoa/bridged_native_widget.h"
#include "ui/views/widget/native_widget_mac.h"
@interface CocoaDragDropDataProvider ()
- (id)initWithData:(const ui::OSExchangeData&)data;
- (id)initWithPasteboard:(NSPasteboard*)pasteboard;
@end
@implementation CocoaDragDropDataProvider
std::unique_ptr<ui::OSExchangeData> data_;
- (id)initWithData:(const ui::OSExchangeData&)data {
if ((self = [super init])) {
data_.reset(new OSExchangeData(data.provider().Clone()));
}
return self;
}
- (id)initWithPasteboard:(NSPasteboard*)pasteboard {
if ((self = [super init])) {
data_ = ui::OSExchangeDataProviderMac::CreateDataFromPasteboard(pasteboard);
}
return self;
}
- (ui::OSExchangeData*)data {
return data_.get();
}
// NSPasteboardItemDataProvider protocol implementation.
- (void)pasteboard:(NSPasteboard*)sender
item:(NSPasteboardItem*)item
provideDataForType:(NSString*)type {
const ui::OSExchangeDataProviderMac& provider =
static_cast<const ui::OSExchangeDataProviderMac&>(data_->provider());
NSData* ns_data = provider.GetNSDataForType(type);
[sender setData:ns_data forType:type];
}
@end
namespace views {
DragDropClientMac::DragDropClientMac(BridgedNativeWidget* bridge,
View* root_view)
: drop_helper_(root_view),
operation_(0),
bridge_(bridge),
quit_closure_(base::Closure()) {
DCHECK(bridge);
}
DragDropClientMac::~DragDropClientMac() {}
void DragDropClientMac::StartDragAndDrop(
View* view,
const ui::OSExchangeData& data,
int operation,
ui::DragDropTypes::DragEventSource source) {
data_source_.reset([[CocoaDragDropDataProvider alloc] initWithData:data]);
operation_ = operation;
const ui::OSExchangeDataProviderMac& provider =
static_cast<const ui::OSExchangeDataProviderMac&>(data.provider());
// Synthesize an event for dragging, since we can't be sure that
// [NSApp currentEvent] will return a valid dragging event.
NSWindow* window = bridge_->ns_window();
NSPoint position = [window mouseLocationOutsideOfEventStream];
NSTimeInterval event_time = [[NSApp currentEvent] timestamp];
NSEvent* event = [NSEvent mouseEventWithType:NSLeftMouseDragged
location:position
modifierFlags:NSLeftMouseDraggedMask
timestamp:event_time
windowNumber:[window windowNumber]
context:nil
eventNumber:0
clickCount:1
pressure:1.0];
NSImage* image = gfx::NSImageFromImageSkiaWithColorSpace(
provider.GetDragImage(), base::mac::GetSRGBColorSpace());
base::scoped_nsobject<NSPasteboardItem> item([[NSPasteboardItem alloc] init]);
[item setDataProvider:data_source_.get()
forTypes:ui::OSExchangeDataProviderMac::
SupportedPasteboardTypes()];
base::scoped_nsobject<NSDraggingItem> drag_item(
[[NSDraggingItem alloc] initWithPasteboardWriter:item.get()]);
// Subtract the image's height from the y location so that the mouse will be
// at the upper left corner of the image.
NSRect dragging_frame =
NSMakeRect([event locationInWindow].x,
[event locationInWindow].y - [image size].height,
[image size].width, [image size].height);
[drag_item setDraggingFrame:dragging_frame contents:image];
[bridge_->ns_view() beginDraggingSessionWithItems:@[ drag_item.get() ]
event:event
source:bridge_->ns_view()];
// Since Drag and drop is asynchronous on Mac, we need to spin a nested run
// loop for consistency with other platforms.
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
run_loop.Run();
}
NSDragOperation DragDropClientMac::DragUpdate(id<NSDraggingInfo> sender) {
int drag_operation = ui::DragDropTypes::DRAG_NONE;
// Since dragging from non MacView sources does not generate OSExchangeData,
// we need to generate one based on the provided pasteboard.
if (!data_source_.get()) {
data_source_.reset([[CocoaDragDropDataProvider alloc]
initWithPasteboard:[sender draggingPasteboard]]);
}
drag_operation = drop_helper_.OnDragOver(
*[data_source_ data], LocationInView([sender draggingLocation]),
operation_);
return ui::DragDropTypes::DragOperationToNSDragOperation(drag_operation);
}
NSDragOperation DragDropClientMac::Drop(id<NSDraggingInfo> sender) {
int drag_operation = drop_helper_.OnDrop(
*[data_source_ data], LocationInView([sender draggingLocation]),
operation_);
return ui::DragDropTypes::DragOperationToNSDragOperation(drag_operation);
}
void DragDropClientMac::EndDrag() {
data_source_.reset();
// Allow a test to invoke EndDrag() without spinning the nested run loop.
if (!quit_closure_.is_null()) {
quit_closure_.Run();
quit_closure_.Reset();
}
}
gfx::Point DragDropClientMac::LocationInView(NSPoint point) const {
return gfx::Point(point.x, NSHeight([bridge_->ns_window() frame]) - point.y);
}
} // namespace views
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ui/views/cocoa/drag_drop_client_mac.h"
#import <Cocoa/Cocoa.h>
#include "base/mac/sdk_forward_declarations.h"
#include "base/strings/utf_string_conversions.h"
#import "ui/views/cocoa/bridged_native_widget.h"
#include "ui/views/test/widget_test.h"
#include "ui/views/view.h"
#include "ui/views/widget/native_widget_mac.h"
#include "ui/views/widget/widget.h"
using base::ASCIIToUTF16;
// Mocks the NSDraggingInfo sent to the DragDropClientMac's DragUpdate() and
// Drop() methods. Out of the required methods of the protocol, only
// draggingLocation and draggingPasteboard are used.
@interface MockDraggingInfo : NSObject<NSDraggingInfo> {
NSPasteboard* pasteboard_;
}
@property BOOL animatesToDestination;
@property NSInteger numberOfValidItemsForDrop;
@property NSDraggingFormation draggingFormation;
@property(readonly) NSSpringLoadingHighlight springLoadingHighlight;
@end
@implementation MockDraggingInfo
@synthesize animatesToDestination;
@synthesize numberOfValidItemsForDrop;
@synthesize draggingFormation;
@synthesize springLoadingHighlight;
- (id)initWithPasteboard:(NSPasteboard*)pasteboard {
if ((self = [super init])) {
pasteboard_ = pasteboard;
}
return self;
}
- (NSPoint)draggingLocation {
return NSMakePoint(50, 50);
}
- (NSPasteboard*)draggingPasteboard {
return pasteboard_;
}
- (NSInteger)draggingSequenceNumber {
return 0;
}
- (id)draggingSource {
return nil;
}
- (NSDragOperation)draggingSourceOperationMask {
return NSDragOperationNone;
}
- (NSWindow*)draggingDestinationWindow {
return nil;
}
- (NSArray*)namesOfPromisedFilesDroppedAtDestination:(NSURL*)dropDestination {
return nil;
}
- (NSImage*)draggedImage {
return nil;
}
- (NSPoint)draggedImageLocation {
return NSZeroPoint;
}
- (void)slideDraggedImageTo:(NSPoint)aPoint {
}
- (void)
enumerateDraggingItemsWithOptions:(NSDraggingItemEnumerationOptions)enumOpts
forView:(NSView*)view
classes:(NSArray*)classArray
searchOptions:(NSDictionary*)searchOptions
usingBlock:(void (^)(NSDraggingItem* draggingItem,
NSInteger idx,
BOOL* stop))block {
}
- (void)resetSpringLoading {
}
@end
namespace views {
namespace test {
// View object that will receive and process dropped data from the test.
class DragDropView : public View {
public:
DragDropView() {}
void set_formats(int formats) { formats_ = formats; }
// View:
bool GetDropFormats(
int* formats,
std::set<ui::Clipboard::FormatType>* format_types) override {
*formats |= formats_;
return true;
}
bool CanDrop(const OSExchangeData& data) override { return true; }
int OnDragUpdated(const ui::DropTargetEvent& event) override {
return ui::DragDropTypes::DRAG_COPY;
}
int OnPerformDrop(const ui::DropTargetEvent& event) override {
return ui::DragDropTypes::DRAG_MOVE;
}
private:
// Drop formats accepted by this View object.
int formats_ = 0;
DISALLOW_COPY_AND_ASSIGN(DragDropView);
};
class DragDropClientMacTest : public WidgetTest {
public:
DragDropClientMacTest() : widget_(new Widget) {}
DragDropClientMac* drag_drop_client() { return bridge_->drag_drop_client(); }
NSDragOperation DragUpdate(NSPasteboard* pasteboard) {
DragDropClientMac* client = drag_drop_client();
dragging_info_.reset(
[[MockDraggingInfo alloc] initWithPasteboard:pasteboard]);
return client->DragUpdate(dragging_info_.get());
}
NSDragOperation Drop() {
DragDropClientMac* client = drag_drop_client();
DCHECK(dragging_info_.get());
NSDragOperation operation = client->Drop(dragging_info_.get());
dragging_info_.reset();
return operation;
}
void SetData(OSExchangeData& data) {
drag_drop_client()->data_source_.reset(
[[CocoaDragDropDataProvider alloc] initWithData:data]);
}
// testing::Test:
void SetUp() override {
WidgetTest::SetUp();
widget_ = CreateTopLevelPlatformWidget();
gfx::Rect bounds(0, 0, 100, 100);
widget_->SetBounds(bounds);
bridge_ =
NativeWidgetMac::GetBridgeForNativeWindow(widget_->GetNativeWindow());
widget_->Show();
target_ = new DragDropView();
widget_->GetContentsView()->AddChildView(target_);
target_->SetBoundsRect(bounds);
drag_drop_client()->operation_ = ui::DragDropTypes::DRAG_COPY;
}
void TearDown() override {
widget_->CloseNow();
WidgetTest::TearDown();
}
protected:
Widget* widget_ = nullptr;
BridgedNativeWidget* bridge_ = nullptr;
DragDropView* target_ = nullptr;
base::scoped_nsobject<MockDraggingInfo> dragging_info_;
private:
DISALLOW_COPY_AND_ASSIGN(DragDropClientMacTest);
};
// Tests if the drag and drop target receives the dropped data.
TEST_F(DragDropClientMacTest, BasicDragDrop) {
// Create the drop data
OSExchangeData data;
const base::string16& text = ASCIIToUTF16("text");
data.SetString(text);
SetData(data);
target_->set_formats(ui::OSExchangeData::STRING | ui::OSExchangeData::URL);
// Check if the target receives the data from a drop and returns the expected
// operation.
EXPECT_EQ(DragUpdate(nil), NSDragOperationCopy);
EXPECT_EQ(Drop(), NSDragOperationMove);
}
// Tests if the drag and drop target rejects the dropped data with the
// incorrect format.
TEST_F(DragDropClientMacTest, InvalidFormatDragDrop) {
OSExchangeData data;
const base::string16& text = ASCIIToUTF16("text");
data.SetString(text);
SetData(data);
target_->set_formats(ui::OSExchangeData::URL);
// Check if the target receives the data from a drop and returns the expected
// operation.
EXPECT_EQ(DragUpdate(nil), NSDragOperationNone);
EXPECT_EQ(Drop(), NSDragOperationNone);
}
// Tests if the drag and drop target can accept data without an OSExchangeData
// object.
TEST_F(DragDropClientMacTest, PasteboardToOSExchangeTest) {
target_->set_formats(ui::OSExchangeData::STRING);
NSPasteboard* pasteboard = [NSPasteboard pasteboardWithUniqueName];
// The test should reject the data if the pasteboard is empty.
EXPECT_EQ(DragUpdate(pasteboard), NSDragOperationNone);
EXPECT_EQ(Drop(), NSDragOperationNone);
drag_drop_client()->EndDrag();
// Add valid data to the pasteboard and check to see if the target accepts
// it.
[pasteboard setString:@"text" forType:NSPasteboardTypeString];
EXPECT_EQ(DragUpdate(pasteboard), NSDragOperationCopy);
EXPECT_EQ(Drop(), NSDragOperationMove);
}
} // namespace test
} // namespace views
...@@ -60,6 +60,8 @@ ...@@ -60,6 +60,8 @@
'cocoa/bridged_native_widget.h', 'cocoa/bridged_native_widget.h',
'cocoa/bridged_native_widget.mm', 'cocoa/bridged_native_widget.mm',
'cocoa/bridged_native_widget_owner.h', 'cocoa/bridged_native_widget_owner.h',
'cocoa/drag_drop_client_mac.h',
'cocoa/drag_drop_client_mac.mm',
'cocoa/cocoa_mouse_capture.h', 'cocoa/cocoa_mouse_capture.h',
'cocoa/cocoa_mouse_capture.mm', 'cocoa/cocoa_mouse_capture.mm',
'cocoa/cocoa_mouse_capture_delegate.h', 'cocoa/cocoa_mouse_capture_delegate.h',
...@@ -583,6 +585,7 @@ ...@@ -583,6 +585,7 @@
'bubble/bubble_window_targeter_unittest.cc', 'bubble/bubble_window_targeter_unittest.cc',
'cocoa/bridged_native_widget_unittest.mm', 'cocoa/bridged_native_widget_unittest.mm',
'cocoa/cocoa_mouse_capture_unittest.mm', 'cocoa/cocoa_mouse_capture_unittest.mm',
'cocoa/drag_drop_client_mac_unittest.mm',
'controls/button/blue_button_unittest.cc', 'controls/button/blue_button_unittest.cc',
'controls/button/custom_button_unittest.cc', 'controls/button/custom_button_unittest.cc',
'controls/button/image_button_unittest.cc', 'controls/button/image_button_unittest.cc',
......
...@@ -20,8 +20,10 @@ ...@@ -20,8 +20,10 @@
#include "ui/native_theme/native_theme_mac.h" #include "ui/native_theme/native_theme_mac.h"
#import "ui/views/cocoa/bridged_content_view.h" #import "ui/views/cocoa/bridged_content_view.h"
#import "ui/views/cocoa/bridged_native_widget.h" #import "ui/views/cocoa/bridged_native_widget.h"
#import "ui/views/cocoa/drag_drop_client_mac.h"
#import "ui/views/cocoa/native_widget_mac_nswindow.h" #import "ui/views/cocoa/native_widget_mac_nswindow.h"
#import "ui/views/cocoa/views_nswindow_delegate.h" #import "ui/views/cocoa/views_nswindow_delegate.h"
#include "ui/views/widget/drop_helper.h"
#include "ui/views/widget/widget_delegate.h" #include "ui/views/widget/widget_delegate.h"
#include "ui/views/window/native_frame_view.h" #include "ui/views/window/native_frame_view.h"
...@@ -201,11 +203,9 @@ void NativeWidgetMac::ReorderNativeViews() { ...@@ -201,11 +203,9 @@ void NativeWidgetMac::ReorderNativeViews() {
} }
void NativeWidgetMac::ViewRemoved(View* view) { void NativeWidgetMac::ViewRemoved(View* view) {
// TODO(tapted): Something for drag and drop might be needed here in future. DragDropClientMac* client = bridge_ ? bridge_->drag_drop_client() : nullptr;
// See http://crbug.com/464581. A NOTIMPLEMENTED() here makes a lot of spam, if (client)
// so only emit it when a drag and drop could be likely. client->drop_helper()->ResetTargetViewIfEquals(view);
if (IsMouseButtonDown())
NOTIMPLEMENTED();
} }
void NativeWidgetMac::SetNativeWindowProperty(const char* name, void* value) { void NativeWidgetMac::SetNativeWindowProperty(const char* name, void* value) {
...@@ -507,7 +507,7 @@ void NativeWidgetMac::RunShellDrag(View* view, ...@@ -507,7 +507,7 @@ void NativeWidgetMac::RunShellDrag(View* view,
const gfx::Point& location, const gfx::Point& location,
int operation, int operation,
ui::DragDropTypes::DragEventSource source) { ui::DragDropTypes::DragEventSource source) {
NOTIMPLEMENTED(); bridge_->drag_drop_client()->StartDragAndDrop(view, data, operation, source);
} }
void NativeWidgetMac::SchedulePaintInRect(const gfx::Rect& rect) { void NativeWidgetMac::SchedulePaintInRect(const gfx::Rect& rect) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment