Compare commits
2 Commits
302ae1c338
...
6839870055
Author | SHA1 | Date | |
---|---|---|---|
6839870055 | |||
3c89748eac |
176
addons/dragdrop/dragdrop_windows.c
Normal file
176
addons/dragdrop/dragdrop_windows.c
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
#include <Windows.h>
|
||||||
|
#include <oleidl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "vector.h"
|
||||||
|
#include "_cgo_export.h"
|
||||||
|
|
||||||
|
#define STDULONGMETHODIMP STDMETHODIMP_(ULONG)
|
||||||
|
#define DRAGDROP_MAXFILEPATHSIZE 32767
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
IDropTargetVtbl *_vtbl;
|
||||||
|
long _refCount;
|
||||||
|
|
||||||
|
uint32_t _handle;
|
||||||
|
HWND _windowHandle;
|
||||||
|
} DragDropHandler;
|
||||||
|
|
||||||
|
static void screenToClient(HWND windowHandle, POINTL *point)
|
||||||
|
{
|
||||||
|
ScreenToClient(windowHandle, (LPPOINT)point);
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDMETHODIMP QueryInterface(IDropTarget *this, REFIID riid, LPVOID *ppvObj)
|
||||||
|
{
|
||||||
|
// Always set out parameter to NULL, validating it first.
|
||||||
|
if (!ppvObj)
|
||||||
|
return E_INVALIDARG;
|
||||||
|
*ppvObj = NULL;
|
||||||
|
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDropTarget))
|
||||||
|
{
|
||||||
|
// Increment the reference count and return the pointer.
|
||||||
|
*ppvObj = (LPVOID)this;
|
||||||
|
((DragDropHandler *)this)->_vtbl->AddRef(this);
|
||||||
|
return NOERROR;
|
||||||
|
}
|
||||||
|
return E_NOINTERFACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDULONGMETHODIMP AddRef(IDropTarget *this)
|
||||||
|
{
|
||||||
|
InterlockedIncrement(&((DragDropHandler *)this)->_refCount);
|
||||||
|
return ((DragDropHandler *)this)->_refCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDULONGMETHODIMP Release(IDropTarget *this)
|
||||||
|
{
|
||||||
|
// Decrement the object's internal counter.
|
||||||
|
ULONG ulRefCount = InterlockedDecrement(&((DragDropHandler *)this)->_refCount);
|
||||||
|
if (0 == ((DragDropHandler *)this)->_refCount)
|
||||||
|
{
|
||||||
|
GlobalFree(this);
|
||||||
|
}
|
||||||
|
return ulRefCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDMETHODIMP DragEnter(IDropTarget *this, __RPC__in_opt IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, __RPC__inout DWORD *pdwEffect)
|
||||||
|
{
|
||||||
|
*pdwEffect = DROPEFFECT_COPY;
|
||||||
|
|
||||||
|
wchar_t filePath[DRAGDROP_MAXFILEPATHSIZE];
|
||||||
|
|
||||||
|
FORMATETC format;
|
||||||
|
format.cfFormat = CF_HDROP;
|
||||||
|
format.ptd = NULL;
|
||||||
|
format.dwAspect = DVASPECT_CONTENT;
|
||||||
|
format.lindex = -1;
|
||||||
|
format.tymed = TYMED_HGLOBAL;
|
||||||
|
|
||||||
|
STGMEDIUM medium;
|
||||||
|
|
||||||
|
HRESULT result = pDataObj->lpVtbl->GetData(pDataObj, &format, &medium);
|
||||||
|
if (result != S_OK)
|
||||||
|
return E_UNEXPECTED;
|
||||||
|
HDROP drop = (HDROP)GlobalLock(medium.hGlobal);
|
||||||
|
|
||||||
|
clearFiles(((DragDropHandler *)this)->_handle);
|
||||||
|
UINT numberOfFiles = DragQueryFile(drop, 0xFFFFFFFF, NULL, 0);
|
||||||
|
for (UINT fileIndex = 0; fileIndex < numberOfFiles; fileIndex++)
|
||||||
|
{
|
||||||
|
UINT length = DragQueryFileW(drop, fileIndex, filePath, DRAGDROP_MAXFILEPATHSIZE);
|
||||||
|
addFile(((DragDropHandler *)this)->_handle, &filePath[0], length);
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalUnlock(medium.hGlobal);
|
||||||
|
if (medium.pUnkForRelease != NULL)
|
||||||
|
medium.pUnkForRelease->lpVtbl->Release(medium.pUnkForRelease);
|
||||||
|
|
||||||
|
screenToClient(((DragDropHandler *)this)->_windowHandle, &pt);
|
||||||
|
onDragEnter(((DragDropHandler *)this)->_handle, pt.x, pt.y);
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDMETHODIMP DragOver(IDropTarget *this, DWORD grfKeyState, POINTL pt, __RPC__inout DWORD *pdwEffect)
|
||||||
|
{
|
||||||
|
*pdwEffect = DROPEFFECT_COPY;
|
||||||
|
screenToClient(((DragDropHandler *)this)->_windowHandle, &pt);
|
||||||
|
onDragOver(((DragDropHandler *)this)->_handle, pt.x, pt.y);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDMETHODIMP DragLeave(IDropTarget *this)
|
||||||
|
{
|
||||||
|
onDragLeave(((DragDropHandler *)this)->_handle);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDMETHODIMP Drop(IDropTarget *this, __RPC__in_opt IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, __RPC__inout DWORD *pdwEffect)
|
||||||
|
{
|
||||||
|
*pdwEffect = DROPEFFECT_COPY;
|
||||||
|
screenToClient(((DragDropHandler *)this)->_windowHandle, &pt);
|
||||||
|
onDrop(((DragDropHandler *)this)->_handle, pt.x, pt.y);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const IDropTargetVtbl DragDropHandlerVtbl = {
|
||||||
|
QueryInterface,
|
||||||
|
AddRef,
|
||||||
|
Release,
|
||||||
|
DragEnter,
|
||||||
|
DragOver,
|
||||||
|
DragLeave,
|
||||||
|
Drop,
|
||||||
|
};
|
||||||
|
|
||||||
|
static DragDropHandler *newDragDropHandler(HWND windowHandle, uint32_t handle)
|
||||||
|
{
|
||||||
|
DragDropHandler *handler = malloc(sizeof(DragDropHandler));
|
||||||
|
handler->_vtbl = (IDropTargetVtbl *)&DragDropHandlerVtbl;
|
||||||
|
handler->_refCount = 0;
|
||||||
|
handler->_windowHandle = windowHandle;
|
||||||
|
handler->_handle = handle;
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL dragDropInitialized = FALSE;
|
||||||
|
typedef vector(DragDropHandler *) DragDropHandlers;
|
||||||
|
DragDropHandlers handlers;
|
||||||
|
|
||||||
|
static void VerifyResult(CHAR *component, HRESULT result)
|
||||||
|
{
|
||||||
|
if (result == S_OK)
|
||||||
|
return;
|
||||||
|
printf("%s failed (error code: %d)\n", component, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initDragDrop(void)
|
||||||
|
{
|
||||||
|
if (dragDropInitialized == TRUE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
HRESULT result = OleInitialize(NULL);
|
||||||
|
VerifyResult("OleInitialize", result);
|
||||||
|
|
||||||
|
vector_init(handlers);
|
||||||
|
|
||||||
|
dragDropInitialized = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t RegisterHandler(void *window)
|
||||||
|
{
|
||||||
|
initDragDrop();
|
||||||
|
|
||||||
|
size_t handle = handlers.count + 1;
|
||||||
|
HWND windowHandle = (HWND)window;
|
||||||
|
DragDropHandler *handler = newDragDropHandler(windowHandle, handle);
|
||||||
|
vector_append(DragDropHandler *, handlers, handler);
|
||||||
|
|
||||||
|
DragAcceptFiles(windowHandle, TRUE);
|
||||||
|
HRESULT result = RegisterDragDrop(windowHandle, (IDropTarget *)handler);
|
||||||
|
VerifyResult("RegisterDragDrop", result);
|
||||||
|
|
||||||
|
return (uint32_t)handle;
|
||||||
|
}
|
96
addons/dragdrop/dragdrop_windows.go
Normal file
96
addons/dragdrop/dragdrop_windows.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package dragdrop
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/text/encoding/unicode"
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
|
"opslag.de/schobers/zntg/ui"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo LDFLAGS: -lole32 -luuid
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <oleidl.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
extern uint32_t RegisterHandler(void* windowHandle);
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
//export clearFiles
|
||||||
|
func clearFiles(handle uint32) {
|
||||||
|
handler := handlers[handle]
|
||||||
|
handler.Files = nil
|
||||||
|
handlers[handle] = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
//export addFile
|
||||||
|
func addFile(handle uint32, pathNative *C.wchar_t, pathNativeLength C.UINT) {
|
||||||
|
pathBytes := C.GoBytes(unsafe.Pointer(pathNative), C.int(pathNativeLength)*C.sizeof_wchar_t)
|
||||||
|
decoder := unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewDecoder()
|
||||||
|
path, err := decoder.Bytes(pathBytes)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
handler := handlers[handle]
|
||||||
|
handler.Files = append(handler.Files, string(path))
|
||||||
|
handlers[handle] = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
//export onDragEnter
|
||||||
|
func onDragEnter(handle uint32, x, y C.LONG) {
|
||||||
|
invokeHandler(handle, func(target ui.DragDropEventTarget, files []string) {
|
||||||
|
target.DragEnter(pos(x, y), files)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//export onDragOver
|
||||||
|
func onDragOver(handle uint32, x, y C.LONG) {
|
||||||
|
invokeHandler(handle, func(target ui.DragDropEventTarget, _ []string) {
|
||||||
|
target.DragMove(pos(x, y))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//export onDragLeave
|
||||||
|
func onDragLeave(handle uint32) {
|
||||||
|
invokeHandler(handle, func(target ui.DragDropEventTarget, _ []string) {
|
||||||
|
target.DragLeave()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//export onDrop
|
||||||
|
func onDrop(handle uint32, x, y C.LONG) {
|
||||||
|
invokeHandler(handle, func(target ui.DragDropEventTarget, files []string) {
|
||||||
|
target.Drop(pos(x, y), files)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type handler struct {
|
||||||
|
Target ui.DragDropEventTarget
|
||||||
|
Files []string
|
||||||
|
}
|
||||||
|
|
||||||
|
var handlers map[uint32]handler = map[uint32]handler{}
|
||||||
|
|
||||||
|
func invokeHandler(handle uint32, invoke func(ui.DragDropEventTarget, []string)) {
|
||||||
|
handler := handlers[handle]
|
||||||
|
invoke(handler.Target, handler.Files)
|
||||||
|
}
|
||||||
|
|
||||||
|
func pos(x, y C.LONG) geom.PointF32 { return geom.Pt(int(x), int(y)).ToF32() }
|
||||||
|
|
||||||
|
type provider struct{}
|
||||||
|
|
||||||
|
func (p provider) Register(windowHandle uintptr, target ui.DragDropEventTarget) {
|
||||||
|
handle := C.RegisterHandler(unsafe.Pointer(windowHandle))
|
||||||
|
handlers[uint32(handle)] = handler{target, nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
ui.DefaultDragDropProvider = provider{}
|
||||||
|
}
|
26
addons/dragdrop/vector.h
Normal file
26
addons/dragdrop/vector.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#ifndef __VECTOR_H__
|
||||||
|
#define __VECTOR_H__
|
||||||
|
|
||||||
|
#define vector(type) \
|
||||||
|
struct \
|
||||||
|
{ \
|
||||||
|
type *items; \
|
||||||
|
size_t count; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define vector_init(v) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
(v).items = 0; \
|
||||||
|
(v).count = 0; \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
#define vector_append(type, v, i) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
(v).count++; \
|
||||||
|
(v).items = (type *)realloc((v).items, sizeof(type) * (v).count); \
|
||||||
|
(v).items[(v).count - 1] = i; \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
#endif // __VECTOR_H__
|
@ -7,8 +7,8 @@
|
|||||||
|
|
||||||
#define droppedFilePathSize 32767
|
#define droppedFilePathSize 32767
|
||||||
|
|
||||||
|
BOOL dropHookInitialized = FALSE;
|
||||||
HHOOK nextHook;
|
HHOOK nextHook;
|
||||||
uint32_t nextDropID = 0;
|
|
||||||
|
|
||||||
LRESULT CALLBACK DragAndDropHook(
|
LRESULT CALLBACK DragAndDropHook(
|
||||||
_In_ int nCode,
|
_In_ int nCode,
|
||||||
@ -24,29 +24,40 @@ LRESULT CALLBACK DragAndDropHook(
|
|||||||
case WM_DROPFILES:
|
case WM_DROPFILES:
|
||||||
{
|
{
|
||||||
wchar_t droppedFilePath[droppedFilePathSize];
|
wchar_t droppedFilePath[droppedFilePathSize];
|
||||||
uint32_t dropID = nextDropID++;
|
|
||||||
|
|
||||||
|
clearDrop();
|
||||||
HDROP drop = (HDROP)message->wParam;
|
HDROP drop = (HDROP)message->wParam;
|
||||||
POINT position;
|
|
||||||
DragQueryPoint(drop, &position);
|
|
||||||
droppedFilesPosition(dropID, position.x, position.y);
|
|
||||||
UINT numberOfFiles = DragQueryFile(drop, 0xFFFFFFFF, NULL, 0);
|
UINT numberOfFiles = DragQueryFile(drop, 0xFFFFFFFF, NULL, 0);
|
||||||
for (UINT fileIndex = 0; fileIndex < numberOfFiles; fileIndex++)
|
for (UINT fileIndex = 0; fileIndex < numberOfFiles; fileIndex++)
|
||||||
{
|
{
|
||||||
UINT length = DragQueryFileW(drop, fileIndex, droppedFilePath, droppedFilePathSize);
|
UINT length = DragQueryFileW(drop, fileIndex, droppedFilePath, droppedFilePathSize);
|
||||||
droppedFilesFile(dropID, &droppedFilePath[0], length);
|
addDroppedFile(&droppedFilePath[0], length);
|
||||||
}
|
}
|
||||||
droppedFilesDrop(dropID);
|
POINT position;
|
||||||
|
DragQueryPoint(drop, &position);
|
||||||
|
|
||||||
|
dropFinished(position.x, position.y);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return CallNextHookEx(nextHook, nCode, wParam, lParam);
|
return CallNextHookEx(nextHook, nCode, wParam, lParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void initDropHook(HWND windowHandle)
|
||||||
|
{
|
||||||
|
if (dropHookInitialized == TRUE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DWORD threadId = GetWindowThreadProcessId(windowHandle, NULL);
|
||||||
|
nextHook = SetWindowsHookEx(WH_GETMESSAGE, DragAndDropHook, NULL, threadId);
|
||||||
|
|
||||||
|
dropHookInitialized = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
void SetDragAndDropHook(void *window)
|
void SetDragAndDropHook(void *window)
|
||||||
{
|
{
|
||||||
HWND windowHandle = (HWND)window;
|
HWND windowHandle = (HWND)window;
|
||||||
|
initDropHook(windowHandle);
|
||||||
|
|
||||||
DragAcceptFiles(windowHandle, TRUE);
|
DragAcceptFiles(windowHandle, TRUE);
|
||||||
DWORD threadId = GetWindowThreadProcessId(windowHandle, NULL);
|
|
||||||
nextHook = SetWindowsHookEx(WH_GETMESSAGE, DragAndDropHook, NULL, threadId);
|
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,11 @@
|
|||||||
package drop
|
package drop
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"golang.org/x/text/encoding/unicode"
|
"golang.org/x/text/encoding/unicode"
|
||||||
"opslag.de/schobers/geom"
|
"opslag.de/schobers/geom"
|
||||||
|
"opslag.de/schobers/zntg/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -18,53 +18,48 @@ void SetDragAndDropHook(void* window);
|
|||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
var handler Dropper = nil
|
type handler struct {
|
||||||
var drops map[uint32]droppedFiles = map[uint32]droppedFiles{}
|
Target ui.DragDropEventTarget
|
||||||
|
|
||||||
type droppedFiles struct {
|
|
||||||
X, Y int
|
|
||||||
Files []string
|
Files []string
|
||||||
}
|
}
|
||||||
|
|
||||||
//export droppedFilesPosition
|
var targets map[ui.DragDropEventTarget]struct{} = map[ui.DragDropEventTarget]struct{}{}
|
||||||
func droppedFilesPosition(id C.uint32_t, x, y C.INT) {
|
var droppedFiles []string
|
||||||
files := drops[uint32(id)]
|
|
||||||
files.X = int(x)
|
//export clearDrop
|
||||||
files.Y = int(y)
|
func clearDrop() {
|
||||||
drops[uint32(id)] = files
|
droppedFiles = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//export droppedFilesFile
|
//export dropFinished
|
||||||
func droppedFilesFile(id C.uint32_t, filePath *C.wchar_t, filePathLength C.UINT) {
|
func dropFinished(x, y C.INT) {
|
||||||
files := drops[uint32(id)]
|
for target := range targets {
|
||||||
|
target.Drop(geom.PtF32(float32(x), float32(y)), droppedFiles)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//export addDroppedFile
|
||||||
|
func addDroppedFile(filePath *C.wchar_t, filePathLength C.UINT) {
|
||||||
pathBytes := C.GoBytes(unsafe.Pointer(filePath), C.int(filePathLength)*C.sizeof_wchar_t)
|
pathBytes := C.GoBytes(unsafe.Pointer(filePath), C.int(filePathLength)*C.sizeof_wchar_t)
|
||||||
decoder := unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewDecoder()
|
decoder := unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewDecoder()
|
||||||
path, err := decoder.Bytes(pathBytes)
|
path, err := decoder.Bytes(pathBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
files.Files = append(files.Files, string(path))
|
droppedFiles = append(droppedFiles, string(path))
|
||||||
drops[uint32(id)] = files
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export droppedFilesDrop
|
func RegisterAsDefaultProvider() {
|
||||||
func droppedFilesDrop(id C.uint32_t) {
|
ui.DefaultDragDropProvider = &provider{}
|
||||||
h := handler
|
|
||||||
if h == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
drop, ok := drops[uint32(id)]
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
h.FilesDropped(geom.Pt(drop.X, drop.Y).ToF32(), drop.Files)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Register(dropper Dropper) error {
|
type provider struct{}
|
||||||
if handler != nil {
|
|
||||||
return errors.New(`can only register single dropper`)
|
func (p provider) Register(windowHandle uintptr, target ui.DragDropEventTarget) {
|
||||||
}
|
C.SetDragAndDropHook(unsafe.Pointer(windowHandle))
|
||||||
handler = dropper
|
targets[target] = struct{}{}
|
||||||
C.SetDragAndDropHook(unsafe.Pointer(dropper.WindowHandle()))
|
}
|
||||||
return nil
|
|
||||||
|
func init() {
|
||||||
|
ui.DefaultDragDropProvider = provider{}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
package drop
|
|
||||||
|
|
||||||
import "opslag.de/schobers/geom"
|
|
||||||
|
|
||||||
type Dropper interface {
|
|
||||||
WindowHandle() uintptr
|
|
||||||
|
|
||||||
FilesDropped(geom.PointF32, []string)
|
|
||||||
}
|
|
@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"opslag.de/schobers/allg5"
|
"opslag.de/schobers/allg5"
|
||||||
"opslag.de/schobers/geom"
|
"opslag.de/schobers/geom"
|
||||||
|
"opslag.de/schobers/zntg/addons/drop"
|
||||||
"opslag.de/schobers/zntg/ui"
|
"opslag.de/schobers/zntg/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -47,6 +48,11 @@ func NewRenderer(w, h int, opts allg5.NewDisplayOptions) (*Renderer, error) {
|
|||||||
})
|
})
|
||||||
clean = nil
|
clean = nil
|
||||||
|
|
||||||
|
if ui.DefaultDragDropProvider != nil {
|
||||||
|
// make sure we fall back on simple drop (OLE implementation doesn't seem to work, reason unknown yet).
|
||||||
|
drop.RegisterAsDefaultProvider()
|
||||||
|
}
|
||||||
|
|
||||||
return &Renderer{disp, eq, nil, user, &ui.OSResources{}, dispPos(disp), ui.KeyState{}, ui.KeyModifierNone, ui.MouseCursorDefault}, nil
|
return &Renderer{disp, eq, nil, user, &ui.OSResources{}, dispPos(disp), ui.KeyState{}, ui.KeyModifierNone, ui.MouseCursorDefault}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,6 +158,10 @@ func (r *Renderer) Refresh() {
|
|||||||
r.user.EmitEvent()
|
r.user.EmitEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Renderer) Stamp() float64 {
|
||||||
|
return allg5.GetTime()
|
||||||
|
}
|
||||||
|
|
||||||
// Renderer implementation (lifetime)
|
// Renderer implementation (lifetime)
|
||||||
|
|
||||||
func (r *Renderer) Destroy() error {
|
func (r *Renderer) Destroy() error {
|
||||||
|
@ -205,6 +205,10 @@ func (r *Renderer) Refresh() {
|
|||||||
sdl.PushEvent(e)
|
sdl.PushEvent(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Renderer) Stamp() float64 {
|
||||||
|
return .001 * float64(sdl.GetTicks())
|
||||||
|
}
|
||||||
|
|
||||||
// Lifetime
|
// Lifetime
|
||||||
|
|
||||||
func (r *Renderer) Destroy() error {
|
func (r *Renderer) Destroy() error {
|
||||||
@ -363,7 +367,7 @@ func (r *Renderer) FillRectangle(rect geom.RectangleF32, c color.Color) {
|
|||||||
|
|
||||||
func (r *Renderer) Line(p, q geom.PointF32, color color.Color, thickness float32) {
|
func (r *Renderer) Line(p, q geom.PointF32, color color.Color, thickness float32) {
|
||||||
r.SetDrawColorGo(color)
|
r.SetDrawColorGo(color)
|
||||||
r.renderer.DrawLineF(p.X, p.Y, p.X, p.Y)
|
r.renderer.DrawLineF(p.X, p.Y, q.X, q.Y)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Renderer) Rectangle(rect geom.RectangleF32, c color.Color, thickness float32) {
|
func (r *Renderer) Rectangle(rect geom.RectangleF32, c color.Color, thickness float32) {
|
||||||
|
46
ui/dragdrop.go
Normal file
46
ui/dragdrop.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package ui
|
||||||
|
|
||||||
|
import "opslag.de/schobers/geom"
|
||||||
|
|
||||||
|
type DragDropEventTarget interface {
|
||||||
|
DragEnter(geom.PointF32, []string)
|
||||||
|
DragMove(geom.PointF32)
|
||||||
|
DragLeave()
|
||||||
|
Drop(geom.PointF32, []string)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DragDropProvider interface {
|
||||||
|
Register(windowHandle uintptr, target DragDropEventTarget)
|
||||||
|
}
|
||||||
|
|
||||||
|
var DefaultDragDropProvider DragDropProvider = nil
|
||||||
|
|
||||||
|
type dragDropEventTarget struct {
|
||||||
|
renderer Renderer
|
||||||
|
events []Event
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *dragDropEventTarget) eventBase() EventBase {
|
||||||
|
return EventBase{StampInSeconds: t.renderer.Stamp()}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *dragDropEventTarget) pushEvent(e Event) {
|
||||||
|
t.events = append(t.events, e)
|
||||||
|
t.renderer.Refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *dragDropEventTarget) DragEnter(pos geom.PointF32, files []string) {
|
||||||
|
t.pushEvent(&DisplayDragEnterEvent{EventBase: t.eventBase(), X: pos.X, Y: pos.Y, Files: files})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *dragDropEventTarget) DragMove(pos geom.PointF32) {
|
||||||
|
t.pushEvent(&DisplayDragMoveEnter{EventBase: t.eventBase(), X: pos.X, Y: pos.Y})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *dragDropEventTarget) DragLeave() {
|
||||||
|
t.pushEvent(&DisplayDragLeaveEvent{EventBase: t.eventBase()})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *dragDropEventTarget) Drop(pos geom.PointF32, files []string) {
|
||||||
|
t.pushEvent(&DisplayDropEvent{EventBase: t.eventBase(), X: pos.X, Y: pos.Y, Files: files})
|
||||||
|
}
|
25
ui/event.go
25
ui/event.go
@ -6,6 +6,31 @@ type DisplayCloseEvent struct {
|
|||||||
EventBase
|
EventBase
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DisplayDragEnterEvent struct {
|
||||||
|
EventBase
|
||||||
|
X, Y float32
|
||||||
|
Files []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type DisplayDragLeaveEvent struct {
|
||||||
|
EventBase
|
||||||
|
}
|
||||||
|
|
||||||
|
type DisplayDragMoveEnter struct {
|
||||||
|
EventBase
|
||||||
|
X, Y float32
|
||||||
|
}
|
||||||
|
|
||||||
|
type DisplayDropEvent struct {
|
||||||
|
EventBase
|
||||||
|
X, Y float32
|
||||||
|
Files []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e DisplayDropEvent) Pos() geom.PointF32 {
|
||||||
|
return geom.PtF32(e.X, e.Y)
|
||||||
|
}
|
||||||
|
|
||||||
type DisplayMoveEvent struct {
|
type DisplayMoveEvent struct {
|
||||||
EventBase
|
EventBase
|
||||||
Bounds geom.RectangleF32
|
Bounds geom.RectangleF32
|
||||||
|
122
ui/examples/03_dragdrop/dragdrop.go
Normal file
122
ui/examples/03_dragdrop/dragdrop.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
|
|
||||||
|
_ "opslag.de/schobers/zntg/addons/dragdrop" // import drag & drop functionality
|
||||||
|
_ "opslag.de/schobers/zntg/sdlui" // import the renderer for the UI
|
||||||
|
|
||||||
|
"opslag.de/schobers/zntg/ui"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dropFiles struct {
|
||||||
|
ui.StackPanel
|
||||||
|
|
||||||
|
ctx ui.Context
|
||||||
|
files *ui.Paragraph
|
||||||
|
ping *ping
|
||||||
|
}
|
||||||
|
|
||||||
|
type ping struct {
|
||||||
|
ui.OverlayBase
|
||||||
|
|
||||||
|
circle ui.Texture
|
||||||
|
position geom.PointF32
|
||||||
|
over bool
|
||||||
|
tick time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ping) Render(ctx ui.Context) {
|
||||||
|
elapsed := time.Since(p.tick)
|
||||||
|
const animationDuration = 500 * time.Millisecond
|
||||||
|
if elapsed > animationDuration {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tint := color.Gray{Y: uint8(elapsed * 255 / animationDuration)}
|
||||||
|
center := geom.PtF32(float32(p.circle.Width()), float32(p.circle.Height())).Mul(.5)
|
||||||
|
ctx.Renderer().DrawTexturePointOptions(p.circle, p.position.Sub(center), ui.DrawOptions{Tint: tint})
|
||||||
|
ctx.Animate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dropFiles) Handle(ctx ui.Context, e ui.Event) bool {
|
||||||
|
switch e := e.(type) {
|
||||||
|
case *ui.DisplayDragMoveEnter:
|
||||||
|
d.ping.tick = time.Now()
|
||||||
|
d.ping.position = geom.PtF32(e.X, e.Y)
|
||||||
|
d.ping.over = true
|
||||||
|
d.ctx.Animate()
|
||||||
|
return true
|
||||||
|
case *ui.DisplayDropEvent:
|
||||||
|
d.files.Text = "Files dropped:\n" + strings.Join(e.Files, "\n")
|
||||||
|
d.ping.tick = time.Now()
|
||||||
|
d.ping.position = geom.PtF32(e.X, e.Y)
|
||||||
|
d.ping.over = false
|
||||||
|
d.ctx.Animate()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return d.StackPanel.Handle(ctx, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCircle() ui.ImageSource {
|
||||||
|
const side = 16
|
||||||
|
center := geom.PtF32(.5*side, .5*side)
|
||||||
|
return &ui.AlphaPixelImageSource{
|
||||||
|
ImageAlphaPixelTestFn: func(p geom.PointF32) uint8 {
|
||||||
|
dist := p.Distance(center)
|
||||||
|
if dist < 7 {
|
||||||
|
return 255
|
||||||
|
} else if dist > 8 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return uint8((8 - dist) * 255 * 0.5)
|
||||||
|
},
|
||||||
|
Size: geom.Pt(side, side),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dropFiles) Init(ctx ui.Context) error {
|
||||||
|
d.ctx = ctx
|
||||||
|
|
||||||
|
_, err := ctx.Fonts().CreateFontPath("default", "../resources/font/OpenSans-Regular.ttf", 14)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.files = ui.BuildParagraph("", nil)
|
||||||
|
|
||||||
|
pingCircle, err := ctx.Renderer().CreateTexture(newCircle())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.ping = &ping{circle: pingCircle}
|
||||||
|
ctx.Overlays().AddOnTop("ping", d.ping, true)
|
||||||
|
|
||||||
|
d.Background = color.White
|
||||||
|
d.Children = []ui.Control{
|
||||||
|
&ui.Label{Text: "Drop files on this window!"},
|
||||||
|
d.files,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func run() error {
|
||||||
|
var render, err = ui.NewRendererDefault("Files Drop Example", 800, 600)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer render.Destroy()
|
||||||
|
|
||||||
|
return ui.RunWait(render, ui.DefaultStyle(), &dropFiles{}, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var err = run()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,7 @@ type Renderer interface {
|
|||||||
// Events
|
// Events
|
||||||
PushEvents(t EventTarget, wait bool) bool
|
PushEvents(t EventTarget, wait bool) bool
|
||||||
Refresh()
|
Refresh()
|
||||||
|
Stamp() float64 // in seconds
|
||||||
|
|
||||||
// Lifetime
|
// Lifetime
|
||||||
Destroy() error
|
Destroy() error
|
||||||
|
13
ui/ui.go
13
ui/ui.go
@ -26,6 +26,13 @@ func RunWait(r Renderer, s *Style, view Control, wait bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dragDropTarget := &dragDropEventTarget{renderer: r}
|
||||||
|
dragDrop := DefaultDragDropProvider
|
||||||
|
if dragDrop != nil {
|
||||||
|
dragDrop.Register(r.WindowHandle(), dragDropTarget)
|
||||||
|
}
|
||||||
|
|
||||||
anim := time.NewTicker(30 * time.Millisecond)
|
anim := time.NewTicker(30 * time.Millisecond)
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
@ -62,6 +69,12 @@ func RunWait(r Renderer, s *Style, view Control, wait bool) error {
|
|||||||
} else {
|
} else {
|
||||||
ctx.tooltip.Text = tooltip
|
ctx.tooltip.Text = tooltip
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dragDropEvents := dragDropTarget.events
|
||||||
|
dragDropTarget.events = nil
|
||||||
|
for _, e := range dragDropEvents {
|
||||||
|
ctx.Handle(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user