[windows] Drag-n-drop support

This commit is contained in:
Lea Anthony
2023-10-21 11:21:10 +11:00
parent 8e0671306a
commit 8463c01123
21 changed files with 608 additions and 90 deletions
+11
View File
@@ -13,6 +13,17 @@ Drop Files onto this window...
</body>
<script>
document.body.addEventListener('drop', function(e) {
// Note that postMessageWithAdditionalObjects does not accept a single object,
// but only accepts an ArrayLike object.
// However, input.files is type FileList, which is already an ArrayLike object so
// no conversion to array is needed.
const currentFiles = input.files;
chrome.webview.postMessageWithAdditionalObjects("FilesDropped", currentFiles);
e.preventDefault();
e.stopPropagation();
return false;
});
wails.Events.On("files", function(event) {
let resultsHTML = "";
event.data.forEach(function(file) {
+2 -2
View File
@@ -35,13 +35,13 @@ func main() {
EnableDragAndDrop: true,
})
window.On(events.FilesDropped, func(event *application.WindowEvent) {
window.On(events.Common.WindowFilesDropped, func(event *application.WindowEvent) {
files := event.Context().DroppedFiles()
app.Events.Emit(&application.WailsEvent{
Name: "files",
Data: files,
})
log.Printf("[Go] FilesDropped received: %+v\n", files)
app.Logger.Info("Files Dropped!", "files", files)
})
err := app.Run()
+3 -1
View File
@@ -28,9 +28,10 @@ require (
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
github.com/pkg/errors v0.9.1
github.com/pterm/pterm v0.12.51
github.com/rodrigocfd/windigo v0.0.0-20230809154420-8faa606d9f5f
github.com/samber/lo v1.38.1
github.com/tc-hib/winres v0.1.6
github.com/wailsapp/go-webview2 v1.0.8
github.com/wailsapp/go-webview2 v1.0.9-0.20231016103125-072d9d56c9b2
github.com/wailsapp/mimetype v1.4.1
golang.org/x/net v0.10.0
golang.org/x/sys v0.13.0
@@ -108,3 +109,4 @@ require (
)
replace github.com/ebitengine/purego v0.4.0-alpha.4 => github.com/tmclane/purego v0.0.0-20230818202843-0b72c8c9140f
replace github.com/wailsapp/go-webview2 v1.0.9-0.20231016103125-072d9d56c9b2 => ..\..\go-webview2
+4 -2
View File
@@ -314,6 +314,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qq
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rodrigocfd/windigo v0.0.0-20230809154420-8faa606d9f5f h1:jIXpgDE/hOglaKyTP5ZRqe6rhcJx2dtCan2ncR66qhU=
github.com/rodrigocfd/windigo v0.0.0-20230809154420-8faa606d9f5f/go.mod h1:Vw0QLpSedVsbAFWYIVE/Zb1pa6XPIgwIMTUQQoUG8Wo=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97 h1:3RPlVWzZ/PDqmVuf/FKHARG5EMid/tl7cv54Sw/QRVY=
github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
@@ -346,8 +348,8 @@ github.com/tc-hib/winres v0.1.6 h1:qgsYHze+BxQPEYilxIz/KCQGaClvI2+yLBAZs+3+0B8=
github.com/tc-hib/winres v0.1.6/go.mod h1:pe6dOR40VOrGz8PkzreVKNvEKnlE8t4yR8A8naL+t7A=
github.com/tmclane/purego v0.0.0-20230818202843-0b72c8c9140f h1:/HXk9aFXP97CJRzOIphm4pzySmJLqIMhKu1kD5usz1E=
github.com/tmclane/purego v0.0.0-20230818202843-0b72c8c9140f/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
github.com/wailsapp/go-webview2 v1.0.8 h1:hyoFPlMSfb/NM64wuVbgBaq1MASJjqsSUYhN+Rbcr9Y=
github.com/wailsapp/go-webview2 v1.0.8/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo=
github.com/wailsapp/go-webview2 v1.0.9-0.20231016103125-072d9d56c9b2 h1:FDoMl9tSWP2bjfhARy8zaH2gRxq98TIleMmCHq6YuNo=
github.com/wailsapp/go-webview2 v1.0.9-0.20231016103125-072d9d56c9b2/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo=
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
@@ -22,6 +22,10 @@ export const EventTypes = {
WindowClose: "windows:WindowClose",
WindowSetFocus: "windows:WindowSetFocus",
WindowKillFocus: "windows:WindowKillFocus",
WindowDragDrop: "windows:WindowDragDrop",
WindowDragEnter: "windows:WindowDragEnter",
WindowDragLeave: "windows:WindowDragLeave",
WindowDragOver: "windows:WindowDragOver",
},
Mac: {
ApplicationDidBecomeActive: "mac:ApplicationDidBecomeActive",
@@ -167,6 +171,7 @@ export const EventTypes = {
WindowShow: "common:WindowShow",
WindowHide: "common:WindowHide",
WindowDPIChanged: "common:WindowDPIChanged",
WindowFilesDropped: "common:WindowFilesDropped",
ThemeChanged: "common:ThemeChanged",
},
};
+7
View File
@@ -191,6 +191,13 @@ type dragAndDropMessage struct {
var windowDragAndDropBuffer = make(chan *dragAndDropMessage)
func addDragAndDropMessage(windowId uint, filenames []string) {
windowDragAndDropBuffer <- &dragAndDropMessage{
windowId: windowId,
filenames: filenames,
}
}
var _ webview.Request = &webViewAssetRequest{}
const webViewRequestHeaderWindowId = "x-wails-window-id"
@@ -3,7 +3,6 @@ package application
var blankApplicationEventContext = &ApplicationEventContext{}
const (
// FilesDropped is the event name for when files are dropped on the window
openedFiles = "openedFiles"
)
@@ -3,7 +3,6 @@ package application
var blankWindowEventContext = &WindowEventContext{}
const (
// FilesDropped is the event name for when files are dropped on the window
droppedFiles = "droppedFiles"
)
@@ -1,8 +1,24 @@
package application
import "github.com/wailsapp/wails/v3/pkg/events"
import (
"github.com/wailsapp/wails/v3/pkg/events"
)
type BackdropType int32
type DragEffect int32
const (
// DragEffectNone is used to indicate that the drop target cannot accept the data.
DragEffectNone DragEffect = 1
// DragEffectCopy is used to indicate that the data is copied to the drop target.
DragEffectCopy DragEffect = 2
// DragEffectMove is used to indicate that the data is removed from the drag source.
DragEffectMove DragEffect = 3
// DragEffectLink is used to indicate that a link to the original data is established.
DragEffectLink DragEffect = 4
// DragEffectScroll is used to indicate that the target can be scrolled while dragging to locate a drop position that is not currently visible in the target.
)
const (
Auto BackdropType = 0
@@ -74,6 +90,10 @@ type WindowsWindow struct {
// Menu is the menu to use for the window.
Menu *Menu
// Drag Cursor Effects
OnEnterEffect DragEffect
OnOverEffect DragEffect
}
type Theme int
+1 -1
View File
@@ -1010,7 +1010,7 @@ func (w *WebviewWindow) HandleDragAndDropMessage(filenames []string) {
ctx := newWindowEventContext()
ctx.setDroppedFiles(filenames)
thisEvent.ctx = ctx
for _, listener := range w.eventListeners[uint(events.FilesDropped)] {
for _, listener := range w.eventListeners[uint(events.Common.WindowFilesDropped)] {
listener.callback(thisEvent)
}
}
@@ -14,6 +14,7 @@ import (
"path"
"strconv"
"strings"
"sync"
"time"
"unicode/utf16"
"unsafe"
@@ -60,6 +61,8 @@ type windowsWebviewWindow struct {
resizeBorderWidth int32
resizeBorderHeight int32
focusingChromium bool
dropTarget *w32.DropTarget
onceDo sync.Once
}
func (w *windowsWebviewWindow) handleKeyEvent(_ string) {
@@ -391,6 +394,9 @@ func (w *windowsWebviewWindow) relativePosition() (int, int) {
func (w *windowsWebviewWindow) destroy() {
// Not sure if we have anything to destroy...
if w.dropTarget != nil {
w.dropTarget.Release()
}
}
func (w *windowsWebviewWindow) reload() {
@@ -915,6 +921,7 @@ func (w *windowsWebviewWindow) isActive() bool {
}
func (w *windowsWebviewWindow) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
w.onceDo.Do(w.onCreate)
switch msg {
case w32.WM_ACTIVATE:
if int(wparam&0xffff) == w32.WA_INACTIVE {
@@ -1006,6 +1013,7 @@ func (w *windowsWebviewWindow) WndProc(msg uint32, wparam, lparam uintptr) uintp
if hasConstraints {
return 0
}
case w32.WM_DPICHANGED:
newWindowSize := (*w32.RECT)(unsafe.Pointer(lparam))
w32.SetWindowPos(w.hwnd,
@@ -1299,6 +1307,7 @@ func (w *windowsWebviewWindow) setupChromium() {
}
chromium.MessageCallback = w.processMessage
chromium.MessageWithAdditionalObjectsCallback = w.processMessageWithAdditionalObjects
chromium.WebResourceRequestedCallback = w.processRequest
chromium.NavigationCompletedCallback = w.navigationCompleted
chromium.AcceleratorKeyCallback = func(vkey uint) bool {
@@ -1318,6 +1327,49 @@ func (w *windowsWebviewWindow) setupChromium() {
}
}
if chromium.HasCapability(edge.AllowExternalDrop) {
err := chromium.AllowExternalDrag(false)
if err != nil {
globalApplication.fatal(err.Error())
}
if opts.OnEnterEffect != 0 {
w.dropTarget.OnEnterEffect = convertEffect(opts.OnEnterEffect)
}
if opts.OnOverEffect != 0 {
w.dropTarget.OnOverEffect = convertEffect(opts.OnOverEffect)
}
if w.parent.options.EnableDragAndDrop {
w.dropTarget = w32.NewDropTarget()
w.dropTarget.OnDrop = func(files []string) {
w.parent.emit(events.Windows.WindowDragDrop)
windowDragAndDropBuffer <- &dragAndDropMessage{
windowId: windowID,
filenames: files,
}
}
w.dropTarget.OnEnter = func() {
w.parent.emit(events.Windows.WindowDragEnter)
}
w.dropTarget.OnLeave = func() {
w.parent.emit(events.Windows.WindowDragLeave)
}
w.dropTarget.OnOver = func() {
w.parent.emit(events.Windows.WindowDragOver)
}
}
}
// We will get round to this
//if chromium.HasCapability(edge.AllowExternalDrop) {
// err := chromium.AllowExternalDrag(w.parent.options.EnableDragAndDrop)
// if err != nil {
// globalApplication.fatal(err.Error())
// }
// if w.parent.options.EnableDragAndDrop {
// chromium.MessageWithAdditionalObjectsCallback = w.processMessageWithAdditionalObjects
// }
//}
chromium.Resize()
settings, err := chromium.GetSettings()
if err != nil {
@@ -1408,6 +1460,19 @@ func (w *windowsWebviewWindow) setupChromium() {
}
func convertEffect(effect DragEffect) w32.DWORD {
switch effect {
case DragEffectCopy:
return w32.DROPEFFECT_COPY
case DragEffectMove:
return w32.DROPEFFECT_MOVE
case DragEffectLink:
return w32.DROPEFFECT_LINK
default:
return w32.DROPEFFECT_NONE
}
}
func (w *windowsWebviewWindow) flash(enabled bool) {
w32.FlashWindow(w.hwnd, enabled)
}
@@ -1484,6 +1549,59 @@ func (w *windowsWebviewWindow) processKeyBinding(vkey uint) bool {
}
func (w *windowsWebviewWindow) processMessageWithAdditionalObjects(message string, sender *edge.ICoreWebView2, args *edge.ICoreWebView2WebMessageReceivedEventArgs) {
if strings.HasPrefix(message, "FilesDropped") {
objs, err := args.GetAdditionalObjects()
if err != nil {
globalApplication.error(err.Error())
return
}
defer objs.Release()
count, err := objs.GetCount()
if err != nil {
globalApplication.error(err.Error())
return
}
var filenames []string
for i := uint32(0); i < count; i++ {
_file, err := objs.GetValueAtIndex(i)
if err != nil {
globalApplication.error("cannot get value at %d : %s", i, err.Error())
return
}
file := (*edge.ICoreWebView2File)(unsafe.Pointer(_file))
defer file.Release()
filepath, err := file.GetPath()
if err != nil {
globalApplication.error("cannot get path for object at %d : %s", i, err.Error())
return
}
filenames = append(filenames, filepath)
}
addDragAndDropMessage(w.parent.id, filenames)
return
}
}
func (w *windowsWebviewWindow) onCreate() {
// Register DnD
InvokeSync(func() {
if w.parent.options.EnableDragAndDrop {
err := w32.RegisterDragDrop(w.hwnd, w.dropTarget)
if err != nil {
globalApplication.error("Error registering drag and drop: " + err.Error())
}
}
})
}
func ScaleWithDPI(pixels int, dpi uint) int {
return (pixels * int(dpi)) / 96
}
+53 -42
View File
@@ -3,10 +3,6 @@ package events
type ApplicationEventType uint
type WindowEventType uint
const (
FilesDropped WindowEventType = iota
)
var Common = newCommonEvents()
type commonEvents struct {
@@ -28,30 +24,32 @@ type commonEvents struct {
WindowShow WindowEventType
WindowHide WindowEventType
WindowDPIChanged WindowEventType
WindowFilesDropped WindowEventType
ThemeChanged ApplicationEventType
}
func newCommonEvents() commonEvents {
return commonEvents{
ApplicationStarted: 1168,
WindowMaximise: 1169,
WindowUnMaximise: 1170,
WindowFullscreen: 1171,
WindowUnFullscreen: 1172,
WindowRestore: 1173,
WindowMinimise: 1174,
WindowUnMinimise: 1175,
WindowClosing: 1176,
WindowZoom: 1177,
WindowZoomIn: 1178,
WindowZoomOut: 1179,
WindowZoomReset: 1180,
WindowFocus: 1181,
WindowLostFocus: 1182,
WindowShow: 1183,
WindowHide: 1184,
WindowDPIChanged: 1185,
ThemeChanged: 1186,
ApplicationStarted: 1172,
WindowMaximise: 1173,
WindowUnMaximise: 1174,
WindowFullscreen: 1175,
WindowUnFullscreen: 1176,
WindowRestore: 1177,
WindowMinimise: 1178,
WindowUnMinimise: 1179,
WindowClosing: 1180,
WindowZoom: 1181,
WindowZoomIn: 1182,
WindowZoomOut: 1183,
WindowZoomReset: 1184,
WindowFocus: 1185,
WindowLostFocus: 1186,
WindowShow: 1187,
WindowHide: 1188,
WindowDPIChanged: 1189,
WindowFilesDropped: 1190,
ThemeChanged: 1191,
}
}
@@ -335,6 +333,10 @@ type windowsEvents struct {
WindowClose WindowEventType
WindowSetFocus WindowEventType
WindowKillFocus WindowEventType
WindowDragDrop WindowEventType
WindowDragEnter WindowEventType
WindowDragLeave WindowEventType
WindowDragOver WindowEventType
}
func newWindowsEvents() windowsEvents {
@@ -360,6 +362,10 @@ func newWindowsEvents() windowsEvents {
WindowClose: 1165,
WindowSetFocus: 1166,
WindowKillFocus: 1167,
WindowDragDrop: 1168,
WindowDragEnter: 1169,
WindowDragLeave: 1170,
WindowDragOver: 1171,
}
}
@@ -512,23 +518,28 @@ var eventToJS = map[uint]string{
1165: "windows:WindowClose",
1166: "windows:WindowSetFocus",
1167: "windows:WindowKillFocus",
1168: "common:ApplicationStarted",
1169: "common:WindowMaximise",
1170: "common:WindowUnMaximise",
1171: "common:WindowFullscreen",
1172: "common:WindowUnFullscreen",
1173: "common:WindowRestore",
1174: "common:WindowMinimise",
1175: "common:WindowUnMinimise",
1176: "common:WindowClosing",
1177: "common:WindowZoom",
1178: "common:WindowZoomIn",
1179: "common:WindowZoomOut",
1180: "common:WindowZoomReset",
1181: "common:WindowFocus",
1182: "common:WindowLostFocus",
1183: "common:WindowShow",
1184: "common:WindowHide",
1185: "common:WindowDPIChanged",
1186: "common:ThemeChanged",
1168: "windows:WindowDragDrop",
1169: "windows:WindowDragEnter",
1170: "windows:WindowDragLeave",
1171: "windows:WindowDragOver",
1172: "common:ApplicationStarted",
1173: "common:WindowMaximise",
1174: "common:WindowUnMaximise",
1175: "common:WindowFullscreen",
1176: "common:WindowUnFullscreen",
1177: "common:WindowRestore",
1178: "common:WindowMinimise",
1179: "common:WindowUnMinimise",
1180: "common:WindowClosing",
1181: "common:WindowZoom",
1182: "common:WindowZoomIn",
1183: "common:WindowZoomOut",
1184: "common:WindowZoomReset",
1185: "common:WindowFocus",
1186: "common:WindowLostFocus",
1187: "common:WindowShow",
1188: "common:WindowHide",
1189: "common:WindowDPIChanged",
1190: "common:WindowFilesDropped",
1191: "common:ThemeChanged",
}
+5
View File
@@ -142,6 +142,10 @@ windows:WindowUnMinimise
windows:WindowClose
windows:WindowSetFocus
windows:WindowKillFocus
windows:WindowDragDrop
windows:WindowDragEnter
windows:WindowDragLeave
windows:WindowDragOver
common:ApplicationStarted
common:WindowMaximise
common:WindowUnMaximise
@@ -160,4 +164,5 @@ common:WindowLostFocus
common:WindowShow
common:WindowHide
common:WindowDPIChanged
common:WindowFilesDropped
common:ThemeChanged
+55
View File
@@ -0,0 +1,55 @@
//go:build windows
package w32
import (
"golang.org/x/sys/windows"
"syscall"
"unsafe"
)
// ComProc stores a COM procedure.
type ComProc uintptr
// NewComProc creates a new COM proc from a Go function.
func NewComProc(fn interface{}) ComProc {
return ComProc(windows.NewCallback(fn))
}
type EventRegistrationToken struct {
value int64
}
// IUnknown
type IUnknown struct {
Vtbl *IUnknownVtbl
}
type IUnknownVtbl struct {
QueryInterface ComProc
AddRef ComProc
Release ComProc
}
func (i *IUnknownVtbl) CallRelease(this unsafe.Pointer) error {
_, _, err := i.Release.Call(
uintptr(this),
)
if err != windows.ERROR_SUCCESS {
return err
}
return nil
}
type IUnknownImpl interface {
QueryInterface(refiid, object uintptr) uintptr
AddRef() uintptr
Release() uintptr
}
// Call calls a COM procedure.
//
//go:uintptrescapes
func (p ComProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
return syscall.SyscallN(uintptr(p), a...)
}
+168
View File
@@ -0,0 +1,168 @@
//go:build windows
package w32
import (
"golang.org/x/sys/windows"
"syscall"
"unsafe"
)
type IDataObjectVtbl struct {
IUnknownVtbl
GetData ComProc
GetDataHere ComProc
QueryGetData ComProc
GetCanonicalFormatEtc ComProc
SetData ComProc
EnumFormatEtc ComProc
DAdvise ComProc
}
type IDataObject struct {
Vtbl *IDataObjectVtbl
}
func (i *IDataObject) AddRef() uintptr {
refCounter, _, _ := i.Vtbl.AddRef.Call(uintptr(unsafe.Pointer(i)))
return refCounter
}
func (i *IDataObject) GetData(formatEtc *FORMATETC, medium *STGMEDIUM) error {
hr, _, err := i.Vtbl.GetData.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(formatEtc)),
uintptr(unsafe.Pointer(medium)),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
}
return err
}
func (i *IDataObject) GetDataHere(formatEtc *FORMATETC, medium *STGMEDIUM) error {
hr, _, err := i.Vtbl.GetDataHere.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(formatEtc)),
uintptr(unsafe.Pointer(medium)),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
}
return err
}
func (i *IDataObject) QueryGetData(formatEtc *FORMATETC) error {
hr, _, err := i.Vtbl.QueryGetData.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(formatEtc)),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
}
return err
}
func (i *IDataObject) GetCanonicalFormatEtc(inputFormatEtc *FORMATETC, outputFormatEtc *FORMATETC) error {
hr, _, err := i.Vtbl.GetCanonicalFormatEtc.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(inputFormatEtc)),
uintptr(unsafe.Pointer(outputFormatEtc)),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
}
return err
}
func (i *IDataObject) SetData(formatEtc *FORMATETC, medium *STGMEDIUM, release bool) error {
hr, _, err := i.Vtbl.SetData.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(formatEtc)),
uintptr(unsafe.Pointer(medium)),
uintptr(BoolToBOOL(release)),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
}
return err
}
func (i *IDataObject) EnumFormatEtc(dwDirection uint32, enumFormatEtc **IEnumFORMATETC) error {
hr, _, err := i.Vtbl.EnumFormatEtc.Call(
uintptr(unsafe.Pointer(i)),
uintptr(dwDirection),
uintptr(unsafe.Pointer(enumFormatEtc)),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
}
return err
}
func (i *IDataObject) DAdvise(formatEtc *FORMATETC, advf uint32, adviseSink *IAdviseSink, pdwConnection *uint32) error {
hr, _, err := i.Vtbl.DAdvise.Call(
uintptr(unsafe.Pointer(i)),
uintptr(unsafe.Pointer(formatEtc)),
uintptr(advf),
uintptr(unsafe.Pointer(adviseSink)),
uintptr(unsafe.Pointer(pdwConnection)),
)
if windows.Handle(hr) != windows.S_OK {
return syscall.Errno(hr)
}
return err
}
type DVTargetDevice struct {
TdSize uint32
TdDriverNameOffset uint16
TdDeviceNameOffset uint16
TdPortNameOffset uint16
TdExtDevmodeOffset uint16
TdData [1]byte
}
type FORMATETC struct {
CfFormat uint16
Ptd *DVTargetDevice
DwAspect uint32
Lindex int32
Tymed Tymed
}
type Tymed uint32
const (
TYMED_HGLOBAL Tymed = 1
TYMED_FILE Tymed = 2
TYMED_ISTREAM Tymed = 4
TYMED_ISTORAGE Tymed = 8
TYMED_GDI Tymed = 16
TYMED_MFPICT Tymed = 32
TYMED_ENHMF Tymed = 64
TYMED_NULL Tymed = 0
)
type STGMEDIUM struct {
Tymed Tymed
Union uintptr
PUnkForRelease IUnknownImpl
}
func (s STGMEDIUM) FileName() string {
if s.Tymed != TYMED_FILE {
return ""
}
return windows.UTF16PtrToString((*uint16)(unsafe.Pointer(s.Union)))
}
func (s STGMEDIUM) Release() {
if s.PUnkForRelease != nil {
s.PUnkForRelease.Release()
}
}
type IEnumFORMATETC struct{}
type IAdviseSink struct{}
type IEnumStatData struct{}
+129
View File
@@ -0,0 +1,129 @@
//go:build windows
package w32
import (
"github.com/wailsapp/go-webview2/pkg/combridge"
"golang.org/x/sys/windows"
)
const (
DROPEFFECT_NONE = 0
DROPEFFECT_COPY = 1
DROPEFFECT_MOVE = 2
DROPEFFECT_LINK = 4
)
func _NOP(_ uintptr) uintptr {
return uintptr(windows.S_FALSE)
}
func init() {
combridge.RegisterVTable[combridge.IUnknown, iDropTarget](
"{00000122-0000-0000-C000-000000000046}",
_iDropTargetDragEnter,
_iDropTargetDragOver,
_iDropTargetDragLeave,
_iDropTargetDrop,
_NOP,
)
}
func _iDropTargetDragEnter(this uintptr, dataObject *IDataObject, grfKeyState DWORD, point POINT, pdfEffect *DWORD) uintptr {
return combridge.Resolve[iDropTarget](this).DragEnter(dataObject, grfKeyState, point, pdfEffect)
}
func _iDropTargetDragOver(this uintptr, grfKeyState DWORD, point POINT, pdfEffect *DWORD) uintptr {
return combridge.Resolve[iDropTarget](this).DragOver(grfKeyState, point, pdfEffect)
}
func _iDropTargetDragLeave(this uintptr) uintptr {
return combridge.Resolve[iDropTarget](this).DragLeave()
}
func _iDropTargetDrop(this uintptr, dataObject *IDataObject, grfKeyState DWORD, point POINT, pdfEffect *DWORD) uintptr {
return combridge.Resolve[iDropTarget](this).Drop(dataObject, grfKeyState, point, pdfEffect)
}
type iDropTarget interface {
combridge.IUnknown
DragEnter(dataObject *IDataObject, grfKeyState DWORD, point POINT, pdfEffect *DWORD) uintptr
DragOver(grfKeyState DWORD, point POINT, pdfEffect *DWORD) uintptr
DragLeave() uintptr
Drop(dataObject *IDataObject, grfKeyState DWORD, point POINT, pdfEffect *DWORD) uintptr
}
var _ iDropTarget = &DropTarget{}
type DropTarget struct {
combridge.IUnknownImpl
OnEnterEffect DWORD
OnOverEffect DWORD
OnEnter func()
OnLeave func()
OnOver func()
OnDrop func(filenames []string)
}
func NewDropTarget() *DropTarget {
return &DropTarget{
OnEnterEffect: DROPEFFECT_COPY,
OnOverEffect: DROPEFFECT_COPY,
}
}
func (d *DropTarget) DragEnter(dataObject *IDataObject, grfKeyState DWORD, point POINT, pdfEffect *DWORD) uintptr {
*pdfEffect = d.OnEnterEffect
if d.OnEnter != nil {
d.OnEnter()
}
return uintptr(windows.S_OK)
}
func (d *DropTarget) DragOver(grfKeyState DWORD, point POINT, pdfEffect *DWORD) uintptr {
*pdfEffect = d.OnOverEffect
if d.OnOver != nil {
d.OnOver()
}
return uintptr(windows.S_OK)
}
func (d *DropTarget) DragLeave() uintptr {
if d.OnLeave != nil {
d.OnLeave()
}
return uintptr(windows.S_OK)
}
func (d *DropTarget) Drop(dataObject *IDataObject, grfKeyState DWORD, point POINT, pdfEffect *DWORD) uintptr {
if d.OnDrop == nil {
return uintptr(windows.S_OK)
}
// Extract filenames from dataObject
var filenames []string
var formatETC = FORMATETC{
CfFormat: CF_HDROP,
Tymed: TYMED_HGLOBAL,
}
var stgMedium STGMEDIUM
err := dataObject.GetData(&formatETC, &stgMedium)
if err != nil && err != windows.ERROR_SUCCESS {
return uintptr(windows.S_FALSE)
}
defer stgMedium.Release()
hDrop := stgMedium.Union
_, numFiles := DragQueryFile(hDrop, 0xFFFFFFFF)
for i := uint(0); i < numFiles; i++ {
filename, _ := DragQueryFile(hDrop, i)
filenames = append(filenames, filename)
}
d.OnDrop(filenames)
return uintptr(windows.S_OK)
}
-29
View File
@@ -1,29 +0,0 @@
//go:build windows
/*
* Copyright (C) 2019 Tad Vizbaras. All Rights Reserved.
* Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved.
*/
package w32
type pIUnknownVtbl struct {
pQueryInterface uintptr
pAddRef uintptr
pRelease uintptr
}
type IUnknown struct {
lpVtbl *pIUnknownVtbl
}
func (this *IUnknown) QueryInterface(id *GUID) *IDispatch {
return ComQueryInterface(this, id)
}
func (this *IUnknown) AddRef() int32 {
return ComAddRef(this)
}
func (this *IUnknown) Release() int32 {
return ComRelease(this)
}
+20
View File
@@ -7,6 +7,7 @@
package w32
import (
"github.com/wailsapp/go-webview2/pkg/combridge"
"syscall"
"unsafe"
)
@@ -16,8 +17,10 @@ var (
procCoInitializeEx = modole32.NewProc("CoInitializeEx")
procCoInitialize = modole32.NewProc("CoInitialize")
procOleInitialize = modole32.NewProc("OleInitialize")
procCoUninitialize = modole32.NewProc("CoUninitialize")
procCreateStreamOnHGlobal = modole32.NewProc("CreateStreamOnHGlobal")
procRegisterDragDrop = modole32.NewProc("RegisterDragDrop")
)
func CoInitializeEx(coInit uintptr) HRESULT {
@@ -63,3 +66,20 @@ func CreateStreamOnHGlobal(hGlobal HGLOBAL, fDeleteOnRelease bool) *IStream {
return stream
}
func OleInitialise() {
procOleInitialize.Call()
}
func RegisterDragDrop(hwnd HWND, dropTarget *DropTarget) error {
dt := combridge.New[iDropTarget](dropTarget)
hr, _, _ := procRegisterDragDrop.Call(
hwnd,
dt.Ref(),
)
if hr != S_OK {
return syscall.Errno(hr)
}
return nil
}
+3 -3
View File
@@ -259,13 +259,13 @@ func SHGetPathFromIDList(idl uintptr) string {
func DragAcceptFiles(hwnd HWND, accept bool) {
procDragAcceptFiles.Call(
uintptr(hwnd),
hwnd,
uintptr(BoolToBOOL(accept)))
}
func DragQueryFile(hDrop HDROP, iFile uint) (fileName string, fileCount uint) {
ret, _, _ := procDragQueryFile.Call(
uintptr(hDrop),
hDrop,
uintptr(iFile),
0,
0)
@@ -276,7 +276,7 @@ func DragQueryFile(hDrop HDROP, iFile uint) (fileName string, fileCount uint) {
buf := make([]uint16, fileCount+1)
ret, _, _ := procDragQueryFile.Call(
uintptr(hDrop),
hDrop,
uintptr(iFile),
uintptr(unsafe.Pointer(&buf[0])),
uintptr(fileCount+1))
+3 -3
View File
@@ -79,7 +79,7 @@ func UTF16PtrToString(cstr *uint16) string {
}
func ComAddRef(unknown *IUnknown) int32 {
ret, _, _ := syscall.Syscall(unknown.lpVtbl.pAddRef, 1,
ret, _, _ := syscall.Syscall(uintptr(unknown.Vtbl.AddRef), 1,
uintptr(unsafe.Pointer(unknown)),
0,
0)
@@ -87,7 +87,7 @@ func ComAddRef(unknown *IUnknown) int32 {
}
func ComRelease(unknown *IUnknown) int32 {
ret, _, _ := syscall.Syscall(unknown.lpVtbl.pRelease, 1,
ret, _, _ := syscall.Syscall(uintptr(unknown.Vtbl.Release), 1,
uintptr(unsafe.Pointer(unknown)),
0,
0)
@@ -96,7 +96,7 @@ func ComRelease(unknown *IUnknown) int32 {
func ComQueryInterface(unknown *IUnknown, id *GUID) *IDispatch {
var disp *IDispatch
hr, _, _ := syscall.Syscall(unknown.lpVtbl.pQueryInterface, 3,
hr, _, _ := syscall.Syscall(uintptr(unknown.Vtbl.QueryInterface), 3,
uintptr(unsafe.Pointer(unknown)),
uintptr(unsafe.Pointer(id)),
uintptr(unsafe.Pointer(&disp)))

Some files were not shown because too many files have changed in this diff Show More