mirror of
https://github.com/wavetermdev/wails.git
synced 2026-08-05 13:53:43 -07:00
[windows] Drag-n-drop support
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
@@ -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",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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",
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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...)
|
||||
}
|
||||
@@ -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{}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user