mirror of
https://github.com/SFML/SFML.git
synced 2024-11-28 22:31:09 +08:00
Add file dropping support
This commit is contained in:
parent
74dfd76b25
commit
89a23ea996
@ -35,6 +35,7 @@
|
|||||||
#include <SFML/System/Vector2.hpp>
|
#include <SFML/System/Vector2.hpp>
|
||||||
|
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
namespace sf
|
namespace sf
|
||||||
@ -294,6 +295,16 @@ public:
|
|||||||
Vector3f value; //!< Current value of the sensor on the X, Y, and Z axes
|
Vector3f value; //!< Current value of the sensor on the X, Y, and Z axes
|
||||||
};
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Files dropped event subtype
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
struct FilesDropped
|
||||||
|
{
|
||||||
|
std::vector<sf::String> filenames; //!< The files which were dropped
|
||||||
|
Vector2i position; //!< The position of the cursor at the time the files were dropped
|
||||||
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Construct from a given `sf::Event` subtype
|
/// \brief Construct from a given `sf::Event` subtype
|
||||||
///
|
///
|
||||||
@ -364,7 +375,8 @@ private:
|
|||||||
TouchBegan,
|
TouchBegan,
|
||||||
TouchMoved,
|
TouchMoved,
|
||||||
TouchEnded,
|
TouchEnded,
|
||||||
SensorChanged>
|
SensorChanged,
|
||||||
|
FilesDropped>
|
||||||
m_data; //!< Event data
|
m_data; //!< Event data
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
|
@ -546,6 +546,18 @@ public:
|
|||||||
VkSurfaceKHR& surface,
|
VkSurfaceKHR& surface,
|
||||||
const VkAllocationCallbacks* allocator = nullptr);
|
const VkAllocationCallbacks* allocator = nullptr);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Enable or disable file dropping.
|
||||||
|
///
|
||||||
|
/// If this is disabled, then when a user drags a file on to the window
|
||||||
|
/// the file will be automatically denied. When this is enabled, the file
|
||||||
|
/// will be accepted, no matter the type
|
||||||
|
///
|
||||||
|
/// \param enabled True to enable, false to disable
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
void setFileDroppingEnabled(bool enabled = true);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Function called after the window has been created
|
/// \brief Function called after the window has been created
|
||||||
|
@ -1608,6 +1608,9 @@ void WindowImplX11::initialize()
|
|||||||
// Flush the commands queue
|
// Flush the commands queue
|
||||||
XFlush(m_display.get());
|
XFlush(m_display.get());
|
||||||
|
|
||||||
|
// Make sure that file dropping is disabled
|
||||||
|
setFileDroppingEnabled(false);
|
||||||
|
|
||||||
// Add this window to the global list of windows (required for focus request)
|
// Add this window to the global list of windows (required for focus request)
|
||||||
const std::lock_guard lock(allWindowsMutex);
|
const std::lock_guard lock(allWindowsMutex);
|
||||||
allWindows.push_back(this);
|
allWindows.push_back(this);
|
||||||
@ -1799,6 +1802,146 @@ bool WindowImplX11::processEvent(XEvent& windowEvent)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Specifications for Xdnd: https://wiki.freedesktop.org/www/Specifications/XDND/
|
||||||
|
|
||||||
|
// Drag and drop position update
|
||||||
|
if (windowEvent.xclient.message_type == getAtom("XdndPosition"))
|
||||||
|
{
|
||||||
|
const Atom xdndStatus = XInternAtom(m_display.get(), "XdndStatus", false);
|
||||||
|
|
||||||
|
XEvent message;
|
||||||
|
message.xclient.type = ClientMessage;
|
||||||
|
message.xclient.display = windowEvent.xclient.display;
|
||||||
|
message.xclient.window = m_dropSource;
|
||||||
|
message.xclient.message_type = xdndStatus;
|
||||||
|
message.xclient.format = 32;
|
||||||
|
|
||||||
|
message.xclient.data.l[0] = static_cast<long>(m_window); // The current window
|
||||||
|
|
||||||
|
// Specify if we want the drop or not, and if we want XdndPosition events whenever the mouse moves out of the rectangle
|
||||||
|
message.xclient.data.l[1] = (m_acceptedFileType != None);
|
||||||
|
|
||||||
|
// Send back window rectangle coordinates and width
|
||||||
|
message.xclient.data.l[2] = 0;
|
||||||
|
message.xclient.data.l[3] = 0;
|
||||||
|
|
||||||
|
// Specify action we accept
|
||||||
|
message.xclient.data.l[4] = static_cast<long>(getAtom("XdndActionCopy"));
|
||||||
|
|
||||||
|
XSendEvent(m_display.get(), m_dropSource, false, 0, &message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (windowEvent.xclient.message_type == getAtom("XdndEnter"))
|
||||||
|
{
|
||||||
|
// Store the source window
|
||||||
|
m_dropSource = static_cast<::Window>(windowEvent.xclient.data.l[0]);
|
||||||
|
|
||||||
|
m_acceptedFileType = None;
|
||||||
|
|
||||||
|
if (windowEvent.xclient.data.l[1] & 0x1)
|
||||||
|
{
|
||||||
|
// There are more than 3 types supported by the source, so we must get the XdndTypeList
|
||||||
|
Atom actualType = None;
|
||||||
|
int actualFormat = 0;
|
||||||
|
unsigned long numOfItems = 0;
|
||||||
|
unsigned long bytesAfterReturn = 0;
|
||||||
|
unsigned char* data = nullptr;
|
||||||
|
// Get the list of types that the source supports
|
||||||
|
if (XGetWindowProperty(m_display.get(),
|
||||||
|
m_dropSource,
|
||||||
|
getAtom("XdndTypeList"),
|
||||||
|
0,
|
||||||
|
1024,
|
||||||
|
false,
|
||||||
|
AnyPropertyType,
|
||||||
|
&actualType,
|
||||||
|
&actualFormat,
|
||||||
|
&numOfItems,
|
||||||
|
&bytesAfterReturn,
|
||||||
|
&data) == Success)
|
||||||
|
{
|
||||||
|
if (actualType != None)
|
||||||
|
{
|
||||||
|
Atom* supportedAtoms = reinterpret_cast<Atom*>(data);
|
||||||
|
|
||||||
|
// Go through all of them and check if we support any of them
|
||||||
|
for (int i = 0; i < static_cast<int>(numOfItems); i++)
|
||||||
|
{
|
||||||
|
if (canAcceptFileType(supportedAtoms[i]))
|
||||||
|
{
|
||||||
|
m_acceptedFileType = supportedAtoms[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Go through the 3 types that the source supports and check if we support any of them
|
||||||
|
for (int i = 2; i < 5; i++)
|
||||||
|
{
|
||||||
|
if (canAcceptFileType(static_cast<Atom>(windowEvent.xclient.data.l[i])))
|
||||||
|
{
|
||||||
|
m_acceptedFileType = static_cast<Atom>(windowEvent.xclient.data.l[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An item has been dropped
|
||||||
|
if (windowEvent.xclient.message_type == getAtom("XdndDrop"))
|
||||||
|
{
|
||||||
|
// Make sure that an acceptable file type was found
|
||||||
|
if (m_acceptedFileType != None)
|
||||||
|
{
|
||||||
|
// Get the timestamp
|
||||||
|
const auto dropTimestamp = static_cast<::Time>(windowEvent.xclient.data.l[2]);
|
||||||
|
|
||||||
|
// Get the selection using the given timestamp
|
||||||
|
XConvertSelection(m_display.get(),
|
||||||
|
getAtom("XdndSelection"),
|
||||||
|
m_acceptedFileType,
|
||||||
|
getAtom("XDND_DATA"),
|
||||||
|
m_window,
|
||||||
|
dropTimestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
XEvent message;
|
||||||
|
|
||||||
|
message.xclient.type = ClientMessage;
|
||||||
|
message.xclient.display = m_display.get();
|
||||||
|
message.xclient.window = m_dropSource;
|
||||||
|
message.xclient.message_type = getAtom("XdndFinished");
|
||||||
|
message.xclient.format = 32;
|
||||||
|
message.xclient.data.l[0] = static_cast<long>(m_window);
|
||||||
|
if (m_acceptedFileType != None)
|
||||||
|
{
|
||||||
|
// Tell the application we copied the data
|
||||||
|
message.xclient.data.l[1] = 1;
|
||||||
|
message.xclient.data.l[2] = static_cast<long>(getAtom("XdndActionCopy"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Tell the application we did nothing
|
||||||
|
message.xclient.data.l[1] = 0;
|
||||||
|
message.xclient.data.l[2] = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
XSendEvent(m_display.get(), m_dropSource, false, NoEventMask, &message);
|
||||||
|
|
||||||
|
m_acceptedFileType = None;
|
||||||
|
m_dropSource = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The cursor left the window, so make sure we clean up
|
||||||
|
if (windowEvent.xclient.message_type == getAtom("XdndLeave"))
|
||||||
|
{
|
||||||
|
m_acceptedFileType = None;
|
||||||
|
m_dropSource = 0;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2073,6 +2216,92 @@ bool WindowImplX11::processEvent(XEvent& windowEvent)
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XConvertSelection response
|
||||||
|
case SelectionNotify:
|
||||||
|
{
|
||||||
|
if (windowEvent.xclient.message_type == getAtom("XdndSelection"))
|
||||||
|
{
|
||||||
|
// Notification that the current selection owner
|
||||||
|
// has responded to our request
|
||||||
|
|
||||||
|
Atom type = 0;
|
||||||
|
int format = 0;
|
||||||
|
unsigned long items = 0;
|
||||||
|
unsigned long remainingBytes = 0;
|
||||||
|
unsigned char* data = nullptr;
|
||||||
|
|
||||||
|
// The selection owner should have written the selection
|
||||||
|
// data to the specified window property
|
||||||
|
const int result = XGetWindowProperty(m_display.get(),
|
||||||
|
m_window,
|
||||||
|
windowEvent.xselection.property,
|
||||||
|
0,
|
||||||
|
0x7fffffff,
|
||||||
|
False,
|
||||||
|
AnyPropertyType,
|
||||||
|
&type,
|
||||||
|
&format,
|
||||||
|
&items,
|
||||||
|
&remainingBytes,
|
||||||
|
&data);
|
||||||
|
|
||||||
|
String filenames;
|
||||||
|
|
||||||
|
if (result == Success)
|
||||||
|
{
|
||||||
|
// We don't support INCR for now
|
||||||
|
// It is very unlikely that this will be returned
|
||||||
|
// for purely text data transfer anyway
|
||||||
|
if (type != getAtom("INCR", false))
|
||||||
|
{
|
||||||
|
filenames = reinterpret_cast<char*>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
XFree(data);
|
||||||
|
|
||||||
|
// The selection requestor must always delete the property themselves
|
||||||
|
XDeleteProperty(m_display.get(), m_window, windowEvent.xselection.property);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split sf::String into std::vector<sf::String> by the new lines
|
||||||
|
|
||||||
|
std::vector<String> filenamesVector;
|
||||||
|
size_t lastPosition = 0;
|
||||||
|
|
||||||
|
while (filenames.find("\n", lastPosition) != std::string::npos)
|
||||||
|
{
|
||||||
|
filenamesVector.push_back(
|
||||||
|
filenames.substring(lastPosition, filenames.find("\n", lastPosition) - lastPosition + 1));
|
||||||
|
|
||||||
|
lastPosition = filenames.find("\n", lastPosition) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastPosition < filenames.getSize())
|
||||||
|
{
|
||||||
|
filenamesVector.push_back(filenames.substring(lastPosition, filenames.getSize() - lastPosition));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String& filename : filenamesVector)
|
||||||
|
{
|
||||||
|
// To signify that it is giving a file, a program may put file:// at the start, so remove it
|
||||||
|
if (filename.find("file://") == 0)
|
||||||
|
{
|
||||||
|
filename = filename.substring(7, filename.getSize() - 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The last character can be a newline for file lists, so remove it if it is there
|
||||||
|
while (filename[filename.getSize() - 1] == '\n' || filename[filename.getSize() - 1] == '\r')
|
||||||
|
{
|
||||||
|
filename = filename.substring(0, filename.getSize() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pushEvent(Event::FilesDropped{filenamesVector, Mouse::getPosition()});
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -2171,4 +2400,58 @@ void WindowImplX11::setWindowSizeConstraints() const
|
|||||||
XSetWMNormalHints(m_display.get(), m_window, &sizeHints);
|
XSetWMNormalHints(m_display.get(), m_window, &sizeHints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
void WindowImplX11::setFileDroppingEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
// Xdnd does not work on Wayland, so we check if Wayland is currently active before we enable Xdnd
|
||||||
|
// Checking if this exists isn't a perfect solution, as a user could set this
|
||||||
|
// in their environment variables, but it's better than crashing
|
||||||
|
|
||||||
|
const char* value = getenv("WAYLAND_DISPLAY");
|
||||||
|
|
||||||
|
// If this variable exists, then that (usually) means that wayland is being used instead of X11, so don't turn on file dropping
|
||||||
|
if (value != nullptr)
|
||||||
|
{
|
||||||
|
// If we are enabling it give it an error, but don't give an error if we are disabling it
|
||||||
|
if (enabled)
|
||||||
|
{
|
||||||
|
sf::err() << "Drag and drop is not supported on Xwayland!" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In order for item dropping to be enabled, the XdndAware property must be set.
|
||||||
|
if (enabled)
|
||||||
|
{
|
||||||
|
Atom xdndVersion = 5;
|
||||||
|
XChangeProperty(m_display.get(),
|
||||||
|
m_window,
|
||||||
|
getAtom("XdndAware"),
|
||||||
|
XA_ATOM,
|
||||||
|
32,
|
||||||
|
PropModeReplace,
|
||||||
|
reinterpret_cast<unsigned char*>(&xdndVersion),
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
XDeleteProperty(m_display.get(), m_window, getAtom("XdndAware"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sf::priv::WindowImplX11::canAcceptFileType(const Atom& fileType)
|
||||||
|
{
|
||||||
|
// We currently only accept uri-lists, but this can be changed if you want to add more types to be supported
|
||||||
|
|
||||||
|
// Array of acceptable file types, this is static so we don't get the Atoms every time
|
||||||
|
static const std::array<Atom, 1> acceptableFileTypes({
|
||||||
|
getAtom("text/uri-list"),
|
||||||
|
});
|
||||||
|
|
||||||
|
return std::any_of(acceptableFileTypes.begin(),
|
||||||
|
acceptableFileTypes.end(),
|
||||||
|
[fileType](const Atom& atom) { return atom == fileType; });
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace sf::priv
|
} // namespace sf::priv
|
||||||
|
@ -197,6 +197,14 @@ public:
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void requestFocus() override;
|
void requestFocus() override;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Enable or disable file dropping.
|
||||||
|
///
|
||||||
|
/// \param enabled True to enable, false to disable
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
void setFileDroppingEnabled(bool enabled = true) override;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Check whether the window has the input focus
|
/// \brief Check whether the window has the input focus
|
||||||
///
|
///
|
||||||
@ -212,6 +220,16 @@ protected:
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void processEvents() override;
|
void processEvents() override;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Check if the given file type can be accepted
|
||||||
|
///
|
||||||
|
/// \param fileType The file type to check
|
||||||
|
///
|
||||||
|
/// \return If the file type is acceptable
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
bool canAcceptFileType(const Atom& fileType);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Request the WM to make the current window active
|
/// \brief Request the WM to make the current window active
|
||||||
@ -336,6 +354,8 @@ private:
|
|||||||
Pixmap m_iconPixmap{}; ///< The current icon pixmap if in use
|
Pixmap m_iconPixmap{}; ///< The current icon pixmap if in use
|
||||||
Pixmap m_iconMaskPixmap{}; ///< The current icon mask pixmap if in use
|
Pixmap m_iconMaskPixmap{}; ///< The current icon mask pixmap if in use
|
||||||
::Time m_lastInputTime{}; ///< Last time we received user input
|
::Time m_lastInputTime{}; ///< Last time we received user input
|
||||||
|
::Window m_dropSource{0}; ///< The window which is giving the dropped item
|
||||||
|
Atom m_acceptedFileType{0}; ///< The MIME type that the other window supports that we also support for file dropping
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sf::priv
|
} // namespace sf::priv
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
// or mingw-w64 addresses files in a case insensitive manner.
|
// or mingw-w64 addresses files in a case insensitive manner.
|
||||||
#include <dbt.h>
|
#include <dbt.h>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
#include <shellapi.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
@ -304,6 +305,11 @@ WindowHandle WindowImplWin32::getNativeHandle() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WindowImplWin32::setFileDroppingEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
DragAcceptFiles(m_handle, enabled);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void WindowImplWin32::processEvents()
|
void WindowImplWin32::processEvents()
|
||||||
{
|
{
|
||||||
@ -1165,6 +1171,38 @@ void WindowImplWin32::processEvent(UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Files dropped event
|
||||||
|
case WM_DROPFILES:
|
||||||
|
{
|
||||||
|
auto* hDrop = reinterpret_cast<HDROP>(wParam);
|
||||||
|
|
||||||
|
const unsigned int count = DragQueryFileW(hDrop, 0xFFFFFFFF, nullptr, 0);
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
std::vector<String> files;
|
||||||
|
|
||||||
|
// Get the filenames as wchar_t then add it to the files vector
|
||||||
|
for (unsigned int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
std::vector<wchar_t> buffer(DragQueryFileW(hDrop, i, nullptr, 0) + 1);
|
||||||
|
|
||||||
|
DragQueryFileW(hDrop, i, buffer.data(), static_cast<UINT>(buffer.size()));
|
||||||
|
|
||||||
|
files.emplace_back(buffer.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let the Windows API know we are done
|
||||||
|
DragFinish(hDrop);
|
||||||
|
|
||||||
|
const Vector2i mousePosition = Mouse::getPosition();
|
||||||
|
|
||||||
|
pushEvent(Event::FilesDropped{files, mousePosition});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,6 +189,14 @@ public:
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
[[nodiscard]] bool hasFocus() const override;
|
[[nodiscard]] bool hasFocus() const override;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Enable or disable file dropping.
|
||||||
|
///
|
||||||
|
/// \param enabled True to enable, false to disable
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
void setFileDroppingEnabled(bool enabled = true) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Process incoming events from the operating system
|
/// \brief Process incoming events from the operating system
|
||||||
|
@ -343,6 +343,12 @@ bool WindowBase::createVulkanSurface(const VkInstance& instance, VkSurfaceKHR& s
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WindowBase::setFileDroppingEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
if (m_impl)
|
||||||
|
m_impl->setFileDroppingEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void WindowBase::onCreate()
|
void WindowBase::onCreate()
|
||||||
{
|
{
|
||||||
|
@ -398,4 +398,10 @@ bool WindowImpl::createVulkanSurface([[maybe_unused]] const VkInstance&
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
void WindowImpl::setFileDroppingEnabled(bool /* enabled */)
|
||||||
|
{
|
||||||
|
// Backup for platforms that don't support drag & drop
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace sf::priv
|
} // namespace sf::priv
|
||||||
|
@ -305,6 +305,18 @@ public:
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
bool createVulkanSurface(const VkInstance& instance, VkSurfaceKHR& surface, const VkAllocationCallbacks* allocator) const;
|
bool createVulkanSurface(const VkInstance& instance, VkSurfaceKHR& surface, const VkAllocationCallbacks* allocator) const;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Enable or disable file dropping.
|
||||||
|
///
|
||||||
|
/// If this is disabled, then when a user drags a file on to the window
|
||||||
|
/// the file will be automatically denied. When this is enabled, the file
|
||||||
|
/// will be accepted, no matter the type
|
||||||
|
///
|
||||||
|
/// \param enabled True to enable, false to disable
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
virtual void setFileDroppingEnabled(bool enabled = true);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Default constructor
|
/// \brief Default constructor
|
||||||
|
@ -140,6 +140,12 @@ class WindowImplCocoa;
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
- (CGFloat)displayScaleFactor;
|
- (CGFloat)displayScaleFactor;
|
||||||
|
|
||||||
|
// Event called by MacOS when a file is dragged on top of of the window
|
||||||
|
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender;
|
||||||
|
|
||||||
|
// Function called by MacOS when a file is dropped on top of the window
|
||||||
|
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface SFOpenGLView (keyboard)
|
@interface SFOpenGLView (keyboard)
|
||||||
|
@ -186,6 +186,9 @@
|
|||||||
// Now that we have a window, set up correctly the scale factor and cursor grabbing
|
// Now that we have a window, set up correctly the scale factor and cursor grabbing
|
||||||
[self updateScaleFactor];
|
[self updateScaleFactor];
|
||||||
[self updateCursorGrabbed]; // update for fullscreen
|
[self updateCursorGrabbed]; // update for fullscreen
|
||||||
|
|
||||||
|
// Register the drag types that this view can accept
|
||||||
|
//[self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -341,6 +344,62 @@
|
|||||||
m_mouseIsIn = NO;
|
m_mouseIsIn = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
|
||||||
|
{
|
||||||
|
// Check if what is being dragged is a file
|
||||||
|
if ([sender.draggingPasteboard.types containsObject:NSFilenamesPboardType])
|
||||||
|
{
|
||||||
|
// Make sure that we can get the filename (not just the contents)
|
||||||
|
if (sender.draggingSourceOperationMask & NSDragOperationLink)
|
||||||
|
{
|
||||||
|
// Return that we can accept a filename
|
||||||
|
return NSDragOperationLink;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return that we can't accept this file
|
||||||
|
return NSDragOperationNone;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender
|
||||||
|
{
|
||||||
|
// Make sure that the dragged item is a file
|
||||||
|
if ([sender.draggingPasteboard.types containsObject:NSFilenamesPboardType])
|
||||||
|
{
|
||||||
|
// Get the filenames from the sender
|
||||||
|
NSArray* files = [sender.draggingPasteboard propertyListForType:NSFilenamesPboardType];
|
||||||
|
|
||||||
|
// Convert to std::vector<std::string>
|
||||||
|
|
||||||
|
std::vector<sf::String> filenames;
|
||||||
|
|
||||||
|
for (NSUInteger i = 0; i < [files count]; i++)
|
||||||
|
{
|
||||||
|
NSObject* item = files[i];
|
||||||
|
|
||||||
|
// Make sure that we can cast the NSObject*
|
||||||
|
if ([item isKindOfClass:[NSMutableString class]])
|
||||||
|
{
|
||||||
|
auto* filename = reinterpret_cast<NSMutableString*>(item);
|
||||||
|
|
||||||
|
filenames.emplace_back([filename UTF8String]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NSPoint mousePosition = [self cursorPositionFromEvent:nil];
|
||||||
|
|
||||||
|
if (m_requester != nullptr)
|
||||||
|
m_requester->handleFileDroppingEvent(filenames, sf::Vector2i{int(mousePosition.x), int(mousePosition.y)});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If somehow we get a drag operation for an item we cant accept, return no
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Everything went well, return yes
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark
|
#pragma mark
|
||||||
#pragma mark Subclassing methods
|
#pragma mark Subclassing methods
|
||||||
|
@ -284,5 +284,17 @@
|
|||||||
[context setView:m_oglView];
|
[context setView:m_oglView];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
- (void)setFileDroppingEnabled:(bool)enabled
|
||||||
|
{
|
||||||
|
if (enabled)
|
||||||
|
{
|
||||||
|
[m_oglView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[m_oglView unregisterDraggedTypes];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -628,4 +628,17 @@
|
|||||||
return static_cast<float>(NSHeight([m_window frame]) - NSHeight([[m_window contentView] frame]));
|
return static_cast<float>(NSHeight([m_window frame]) - NSHeight([[m_window contentView] frame]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
- (void)setFileDroppingEnabled:(bool)enabled
|
||||||
|
{
|
||||||
|
if (enabled)
|
||||||
|
{
|
||||||
|
[m_oglView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[m_oglView unregisterDraggedTypes];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -228,6 +228,16 @@ public:
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void applyContext(NSOpenGLContextRef context) const;
|
void applyContext(NSOpenGLContextRef context) const;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Handle the file dropping event
|
||||||
|
///
|
||||||
|
/// Called by SFOpenGLView to handle the file dropping event
|
||||||
|
///
|
||||||
|
/// \param files The files that were dropped
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
void handleFileDroppingEvent(std::vector<sf::String> files, sf::Vector2i position);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Change the type of the current process
|
/// \brief Change the type of the current process
|
||||||
///
|
///
|
||||||
@ -369,6 +379,18 @@ public:
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
[[nodiscard]] bool hasFocus() const override;
|
[[nodiscard]] bool hasFocus() const override;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Enable or disable file dropping.
|
||||||
|
///
|
||||||
|
/// If this is disabled, then when a user drags a file on to the window
|
||||||
|
/// the file will be automatically denied. When this is enabled, the file
|
||||||
|
/// will be accepted, no matter the type
|
||||||
|
///
|
||||||
|
/// \param enabled True to enable, false to disable
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
void setFileDroppingEnabled(bool enabled = true) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Process incoming events from the operating system
|
/// \brief Process incoming events from the operating system
|
||||||
|
@ -246,6 +246,11 @@ void WindowImplCocoa::windowFocusGained()
|
|||||||
pushEvent(Event::FocusGained{});
|
pushEvent(Event::FocusGained{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WindowImplCocoa::handleFileDroppingEvent(std::vector<sf::String> files, sf::Vector2i position)
|
||||||
|
{
|
||||||
|
pushEvent(Event::FilesDropped{std::move(files), position});
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark
|
#pragma mark
|
||||||
#pragma mark WindowImplCocoa's mouse-event methods
|
#pragma mark WindowImplCocoa's mouse-event methods
|
||||||
|
|
||||||
@ -492,5 +497,10 @@ bool WindowImplCocoa::hasFocus() const
|
|||||||
return [m_delegate hasFocus];
|
return [m_delegate hasFocus];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
void WindowImplCocoa::setFileDroppingEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
[m_delegate setFileDroppingEnabled:enabled];
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace sf::priv
|
} // namespace sf::priv
|
||||||
|
@ -251,6 +251,14 @@ class WindowImplCocoa;
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
- (void)applyContext:(NSOpenGLContext*)context;
|
- (void)applyContext:(NSOpenGLContext*)context;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Enable or disable file dropping
|
||||||
|
///
|
||||||
|
/// \param enable True to enable, false to disable
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
- (void)setFileDroppingEnabled:(bool)enabled;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
Loading…
Reference in New Issue
Block a user