Merge pull request #6333 from electron/drag-item

Add `webContents.startDrag(item)` API
This commit is contained in:
Cheng Zhao 2016-07-04 13:30:56 +09:00 committed by GitHub
commit 83ae14f2ed
8 changed files with 206 additions and 0 deletions

View file

@ -17,6 +17,7 @@
#include "atom/browser/lib/bluetooth_chooser.h"
#include "atom/browser/native_window.h"
#include "atom/browser/net/atom_network_delegate.h"
#include "atom/browser/ui/drag_util.h"
#include "atom/browser/web_contents_permission_helper.h"
#include "atom/browser/web_contents_preferences.h"
#include "atom/browser/web_view_guest_delegate.h"
@ -1205,6 +1206,35 @@ void WebContents::EndFrameSubscription() {
view->EndFrameSubscription();
}
void WebContents::StartDrag(const mate::Dictionary& item,
mate::Arguments* args) {
base::FilePath file;
std::vector<base::FilePath> files;
if (!item.Get("files", &files) && item.Get("file", &file)) {
files.push_back(file);
}
mate::Handle<NativeImage> icon;
if (!item.Get("icon", &icon) && !file.empty()) {
// TODO(zcbenz): Set default icon from file.
}
// Error checking.
if (icon.IsEmpty()) {
args->ThrowError("icon must be set");
return;
}
// Start dragging.
if (!files.empty()) {
base::MessageLoop::ScopedNestableTaskAllower allow(
base::MessageLoop::current());
DragFileItems(files, icon->image(), web_contents()->GetNativeView());
} else {
args->ThrowError("There is nothing to drag");
}
}
void WebContents::OnCursorChange(const content::WebCursor& cursor) {
content::WebCursor::CursorInfo info;
cursor.GetCursorInfo(&info);
@ -1324,6 +1354,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
.SetMethod("beginFrameSubscription",
&WebContents::BeginFrameSubscription)
.SetMethod("endFrameSubscription", &WebContents::EndFrameSubscription)
.SetMethod("startDrag", &WebContents::StartDrag)
.SetMethod("setSize", &WebContents::SetSize)
.SetMethod("isGuest", &WebContents::IsGuest)
.SetMethod("getType", &WebContents::GetType)

View file

@ -142,6 +142,9 @@ class WebContents : public mate::TrackableObject<WebContents>,
void BeginFrameSubscription(mate::Arguments* args);
void EndFrameSubscription();
// Dragging native items.
void StartDrag(const mate::Dictionary& item, mate::Arguments* args);
// Methods for creating <webview>.
void SetSize(const SetSizeParams& params);
bool IsGuest() const;

View file

@ -0,0 +1,24 @@
// Copyright (c) 2016 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_UI_DRAG_UTIL_H_
#define ATOM_BROWSER_UI_DRAG_UTIL_H_
#include <vector>
#include "ui/gfx/image/image.h"
namespace base {
class FilePath;
}
namespace atom {
void DragFileItems(const std::vector<base::FilePath>& files,
const gfx::Image& icon,
gfx::NativeView view);
} // namespace atom
#endif // ATOM_BROWSER_UI_DRAG_UTIL_H_

View file

@ -0,0 +1,58 @@
// Copyright (c) 2016 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#import <Cocoa/Cocoa.h>
#include "atom/browser/ui/drag_util.h"
#include "base/files/file_path.h"
#include "base/strings/sys_string_conversions.h"
namespace atom {
namespace {
// Write information about the file being dragged to the pasteboard.
void AddFilesToPasteboard(NSPasteboard* pasteboard,
const std::vector<base::FilePath>& files) {
NSMutableArray* fileList = [NSMutableArray array];
for (const base::FilePath& file : files)
[fileList addObject:base::SysUTF8ToNSString(file.value())];
[pasteboard declareTypes:[NSArray arrayWithObject:NSFilenamesPboardType]
owner:nil];
[pasteboard setPropertyList:fileList forType:NSFilenamesPboardType];
}
} // namespace
void DragFileItems(const std::vector<base::FilePath>& files,
const gfx::Image& icon,
gfx::NativeView view) {
NSPasteboard* pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
AddFilesToPasteboard(pasteboard, files);
// Synthesize a drag event, since we don't have access to the actual event
// that initiated a drag (possibly consumed by the Web UI, for example).
NSPoint position = [[view window] mouseLocationOutsideOfEventStream];
NSTimeInterval eventTime = [[NSApp currentEvent] timestamp];
NSEvent* dragEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
location:position
modifierFlags:NSLeftMouseDraggedMask
timestamp:eventTime
windowNumber:[[view window] windowNumber]
context:nil
eventNumber:0
clickCount:1
pressure:1.0];
// Run the drag operation.
[[view window] dragImage:icon.ToNSImage()
at:position
offset:NSZeroSize
event:dragEvent
pasteboard:pasteboard
source:view
slideBack:YES];
}
} // namespace atom

View file

@ -0,0 +1,48 @@
// Copyright (c) 2016 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/ui/drag_util.h"
#include "ui/aura/window.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/drag_utils.h"
#include "ui/base/dragdrop/file_info.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/screen.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/public/drag_drop_client.h"
namespace atom {
void DragFileItems(const std::vector<base::FilePath>& files,
const gfx::Image& icon,
gfx::NativeView view) {
// Set up our OLE machinery
ui::OSExchangeData data;
drag_utils::CreateDragImageForFile(files[0], icon.AsImageSkia(), &data);
std::vector<ui::FileInfo> file_infos;
for (const base::FilePath& file : files) {
file_infos.push_back(ui::FileInfo(file, base::FilePath()));
}
data.SetFilenames(file_infos);
aura::Window* root_window = view->GetRootWindow();
if (!root_window || !aura::client::GetDragDropClient(root_window))
return;
gfx::Point location = gfx::Screen::GetScreen()->GetCursorScreenPoint();
// TODO(varunjain): Properly determine and send DRAG_EVENT_SOURCE below.
aura::client::GetDragDropClient(root_window)->StartDragAndDrop(
data,
root_window,
view,
location,
ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK,
ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
}
} // namespace atom

View file

@ -943,6 +943,16 @@ defaults to `false`.
End subscribing for frame presentation events.
### `webContents.startDrag(item)`
* `item` object
* `file` String
* `icon` [NativeImage](native-image.md)
Sets the `item` as dragging item for current drag-drop operation, `file` is the
absolute path of the file to be dragged, and `icon` is the image showing under
the cursor when dragging.
### `webContents.savePage(fullPath, saveType, callback)`
* `fullPath` String - The full file path.

View file

@ -321,6 +321,35 @@ win.setRepresentedFilename('/etc/passwd');
win.setDocumentEdited(true);
```
## Dragging files out of the window
For certain kinds of apps that manipulate on files, it is important to be able
to drag files from Electron to other apps. To implement this feature in your
app, you need to call `webContents.startDrag(item)` API on `ondragstart` event.
In web page:
```html
<a href="#" id="drag">item</a>
<script type="text/javascript" charset="utf-8">
document.getElementById('drag').ondragstart = (event) => {
event.preventDefault()
ipcRenderer.send('ondragstart', '/path/to/item')
}
</script>
```
In the main process:
```javascript
ipcMain.on('ondragstart', (event, filePath) => {
event.sender.startDrag({
file: filePath,
icon: '/path/to/icon.png'
})
})
```
[addrecentdocument]: ../api/app.md#appaddrecentdocumentpath-os-x-windows
[clearrecentdocuments]: ../api/app.md#appclearrecentdocuments-os-x-windows
[setusertaskstasks]: ../api/app.md#appsetusertaskstasks-windows

View file

@ -245,6 +245,9 @@
'atom/browser/ui/atom_menu_model.h',
'atom/browser/ui/cocoa/atom_menu_controller.h',
'atom/browser/ui/cocoa/atom_menu_controller.mm',
'atom/browser/ui/drag_util_mac.mm',
'atom/browser/ui/drag_util_views.cc',
'atom/browser/ui/drag_util.h',
'atom/browser/ui/file_dialog.h',
'atom/browser/ui/file_dialog_gtk.cc',
'atom/browser/ui/file_dialog_mac.mm',