Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/vsg/all.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
#include <vsg/ui/PointerEvent.h>
#include <vsg/ui/PrintEvents.h>
#include <vsg/ui/RecordEvents.h>
#include <vsg/ui/DropEvent.h>
#include <vsg/ui/ScrollWheelEvent.h>
#include <vsg/ui/ShiftEventTime.h>
#include <vsg/ui/TouchEvent.h>
Expand Down
8 changes: 8 additions & 0 deletions include/vsg/core/ConstVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ namespace vsg
class TouchUpEvent;
class TouchMoveEvent;
class ScrollWheelEvent;
class DropEvent;
class DropHoverEvent;
class DropLeaveEvent;
class DropFilesEvent;
class TerminateEvent;
class FrameEvent;

Expand Down Expand Up @@ -453,6 +457,10 @@ namespace vsg
virtual void apply(const TouchUpEvent&);
virtual void apply(const TouchMoveEvent&);
virtual void apply(const ScrollWheelEvent&);
virtual void apply(const DropEvent&);
virtual void apply(const DropHoverEvent&);
virtual void apply(const DropLeaveEvent&);
virtual void apply(const DropFilesEvent&);
virtual void apply(const TerminateEvent&);
virtual void apply(const FrameEvent&);

Expand Down
8 changes: 8 additions & 0 deletions include/vsg/core/Visitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ namespace vsg
class TouchUpEvent;
class TouchMoveEvent;
class ScrollWheelEvent;
class DropEvent;
class DropHoverEvent;
class DropLeaveEvent;
class DropFilesEvent;
class TerminateEvent;
class FrameEvent;

Expand Down Expand Up @@ -453,6 +457,10 @@ namespace vsg
virtual void apply(TouchUpEvent&);
virtual void apply(TouchMoveEvent&);
virtual void apply(ScrollWheelEvent&);
virtual void apply(DropEvent&);
virtual void apply(DropHoverEvent&);
virtual void apply(DropLeaveEvent&);
virtual void apply(DropFilesEvent&);
virtual void apply(TerminateEvent&);
virtual void apply(FrameEvent&);

Expand Down
19 changes: 19 additions & 0 deletions include/vsg/platform/win32/Win32_Window.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
# define NOMINMAX
#endif

#include <vsg/ui/DropEvent.h>
#include <vsg/app/Window.h>
#include <vsg/ui/KeyEvent.h>
#include <vsg/ui/PointerEvent.h>
Expand Down Expand Up @@ -207,10 +208,28 @@ namespace vsgWin32

void _initSurface() override;

/// Register the window with OLE so that it is offered file drops.
void _initDrop();
void _shutdownDrop();

HWND _window;
bool _windowMapped = false;

vsg::ref_ptr<KeyboardMap> _keyboard;

// Drag and drop. The OLE callbacks only record what happened; the events are emitted from
// pollEvents() so that nothing reaches the application from inside a window message.
struct DropTarget;
DropTarget* _dropTarget = nullptr;
bool _dropHovering = false;
bool _dropAccepted = false;
vsg::Paths _dropPaths;
int32_t _dropX = 0;
int32_t _dropY = 0;

// The hover event emitted last frame, kept so that the accept flag the application set on it
// can be read back once the frame that handled it has finished.
vsg::ref_ptr<vsg::DropHoverEvent> _dropHoverEvent;
};

/// Use GetLastError() and FormatMessageA(..) to get the error number and error message and store them in a vsg::Exception.
Expand Down
48 changes: 48 additions & 0 deletions include/vsg/platform/xcb/Xcb_Window.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
#pragma once

#include <vsg/app/Window.h>
#include <vsg/ui/DropEvent.h>
#include <vsg/ui/KeyEvent.h>

#include <xcb/xcb.h>
Expand Down Expand Up @@ -77,12 +78,59 @@ namespace vsgXcb

void _initSurface() override;

/// Set up the XdndAware property so that the window is offered file drops.
void _initXdnd();

/// Handle one XDND ClientMessage or the SelectionNotify that carries the dropped file names,
/// appending any resulting Drop events to bufferedEvents. Returns false if the message was
/// not part of a drag and drop exchange.
bool _handleXdndEvent(const xcb_generic_event_t* event);

/// Send the XdndStatus reply that tells the source whether a drop would be accepted. Sent
/// once per frame while a drag is in progress, so a handler that changes its mind while the
/// cursor is stationary is still able to update the cursor.
void _sendXdndStatus();

xcb_connection_t* _connection = nullptr;
xcb_screen_t* _screen = nullptr;
xcb_window_t _window{};
xcb_atom_t _wmProtocols{};
xcb_atom_t _wmDeleteWindow{};

// Drag and drop. The source only hands over the file names once the button is released, so
// the drop is delivered in two steps: XdndDrop asks for the XdndSelection selection, and the
// DropFilesEvent is emitted when the resulting SelectionNotify arrives.
struct XdndAtoms
{
xcb_atom_t aware{};
xcb_atom_t enter{};
xcb_atom_t position{};
xcb_atom_t status{};
xcb_atom_t leave{};
xcb_atom_t drop{};
xcb_atom_t finished{};
xcb_atom_t selection{};
xcb_atom_t typeList{};
xcb_atom_t actionCopy{};
xcb_atom_t uriList{};
xcb_atom_t property{};
};

XdndAtoms _xdnd;
xcb_window_t _xdndSource = 0;
bool _xdndHovering = false;
bool _xdndDropping = false;
bool _xdndAccepted = false;
bool _xdndAcceptedSent = false;
bool _xdndStatusPending = false;
int32_t _xdndX = 0;
int32_t _xdndY = 0;
vsg::clock::time_point _xdndDropStarted;

// The hover event emitted last frame, kept so that the accept flag the application set on it
// can be read back once the frame that handled it has finished.
vsg::ref_ptr<vsg::DropHoverEvent> _xdndHoverEvent;

bool _windowMapped = false;

xcb_timestamp_t _first_xcb_timestamp = 0;
Expand Down
89 changes: 89 additions & 0 deletions include/vsg/ui/DropEvent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#pragma once

/* <editor-fold desc="MIT License">

Copyright(c) 2025 Robert Osfield

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

</editor-fold> */

#include <vsg/io/Path.h>
#include <vsg/ui/WindowEvent.h>

namespace vsg
{

/// DropEvent is a base class for the drag and drop of files from the desktop onto a Window.
///
/// The window's x, y position is in window coordinates, as for PointerEvent. paths holds the
/// files being dragged; note that under X11 the names are only transferred once the button is
/// released, so paths is empty on the hover events there and only DropFilesEvent is complete.
class VSG_DECLSPEC DropEvent : public Inherit<WindowEvent, DropEvent>
{
public:
DropEvent() {}

DropEvent(Window* in_window, time_point in_time, int32_t in_x, int32_t in_y, const Paths& in_paths = {}) :
Inherit(in_window, in_time),
x(in_x),
y(in_y),
paths(in_paths) {}

int32_t x = 0;
int32_t y = 0;
Paths paths;

void read(Input& input) override;
void write(Output& output) const override;
};
VSG_type_name(vsg::DropEvent);

/// DropHoverEvent is sent each frame while a drag is held over the window.
///
/// Handlers set accept to say whether a drop at this position would be taken, which the window
/// passes back to the source so it can show the right cursor. accept starts false, so a window
/// with no interest in drops rejects them by doing nothing.
class VSG_DECLSPEC DropHoverEvent : public Inherit<DropEvent, DropHoverEvent>
{
public:
DropHoverEvent() {}

DropHoverEvent(Window* in_window, time_point in_time, int32_t in_x, int32_t in_y, const Paths& in_paths = {}) :
Inherit(in_window, in_time, in_x, in_y, in_paths) {}

bool accept = false;

void read(Input& input) override;
void write(Output& output) const override;
};
VSG_type_name(vsg::DropHoverEvent);

/// DropLeaveEvent is sent when the drag leaves the window or is cancelled, and after a drop has
/// been delivered, so a handler has a single place to clear any hover state.
class VSG_DECLSPEC DropLeaveEvent : public Inherit<DropEvent, DropLeaveEvent>
{
public:
DropLeaveEvent() {}

DropLeaveEvent(Window* in_window, time_point in_time, int32_t in_x, int32_t in_y) :
Inherit(in_window, in_time, in_x, in_y) {}
};
VSG_type_name(vsg::DropLeaveEvent);

/// DropFilesEvent is sent when the files have been dropped and their names are known.
class VSG_DECLSPEC DropFilesEvent : public Inherit<DropEvent, DropFilesEvent>
{
public:
DropFilesEvent() {}

DropFilesEvent(Window* in_window, time_point in_time, int32_t in_x, int32_t in_y, const Paths& in_paths) :
Inherit(in_window, in_time, in_x, in_y, in_paths) {}
};
VSG_type_name(vsg::DropFilesEvent);

} // namespace vsg
4 changes: 4 additions & 0 deletions src/vsg/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ set(SOURCES
ui/TouchEvent.cpp
ui/PointerEvent.cpp
ui/ScrollWheelEvent.cpp
ui/DropEvent.cpp
ui/WindowEvent.cpp
ui/RecordEvents.cpp
ui/CollectEvents.cpp
Expand Down Expand Up @@ -324,6 +325,9 @@ if (VSG_SUPPORTS_Windowing)
set(LIBRARIES ${LIBRARIES} PRIVATE ace_ndk.z native_window)
elseif (WIN32)
set(SOURCES ${SOURCES} platform/win32/Win32_Window.cpp)
# ole32 provides OleInitialize/RegisterDragDrop, shell32 the CF_HDROP helpers, both used by
# Win32_Window to receive file drops.
set(LIBRARIES ${LIBRARIES} PRIVATE ole32 shell32)
elseif (IOS)
set(HEADERS ${HEADERS}
${VSG_SOURCE_DIR}/include/vsg/platform/ios/iOS_Window.h
Expand Down
16 changes: 16 additions & 0 deletions src/vsg/core/ConstVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,22 @@ void ConstVisitor::apply(const ScrollWheelEvent& event)
{
apply(static_cast<const WindowEvent&>(event));
}
void ConstVisitor::apply(const DropEvent& event)
{
apply(static_cast<const WindowEvent&>(event));
}
void ConstVisitor::apply(const DropHoverEvent& event)
{
apply(static_cast<const DropEvent&>(event));
}
void ConstVisitor::apply(const DropLeaveEvent& event)
{
apply(static_cast<const DropEvent&>(event));
}
void ConstVisitor::apply(const DropFilesEvent& event)
{
apply(static_cast<const DropEvent&>(event));
}
void ConstVisitor::apply(const TerminateEvent& event)
{
apply(static_cast<const UIEvent&>(event));
Expand Down
16 changes: 16 additions & 0 deletions src/vsg/core/Visitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,22 @@ void Visitor::apply(ScrollWheelEvent& event)
{
apply(static_cast<WindowEvent&>(event));
}
void Visitor::apply(DropEvent& event)
{
apply(static_cast<WindowEvent&>(event));
}
void Visitor::apply(DropHoverEvent& event)
{
apply(static_cast<DropEvent&>(event));
}
void Visitor::apply(DropLeaveEvent& event)
{
apply(static_cast<DropEvent&>(event));
}
void Visitor::apply(DropFilesEvent& event)
{
apply(static_cast<DropEvent&>(event));
}
void Visitor::apply(TerminateEvent& event)
{
apply(static_cast<UIEvent&>(event));
Expand Down
4 changes: 4 additions & 0 deletions src/vsg/io/ObjectFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,10 @@ ObjectFactory::ObjectFactory()
add<vsg::TouchUpEvent>();
add<vsg::TouchMoveEvent>();
add<vsg::ScrollWheelEvent>();
add<vsg::DropEvent>();
add<vsg::DropHoverEvent>();
add<vsg::DropLeaveEvent>();
add<vsg::DropFilesEvent>();
add<vsg::WindowEvent>();
add<vsg::ExposeWindowEvent>();
add<vsg::ConfigureWindowEvent>();
Expand Down
Loading
Loading