Skip to content
Closed
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 examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ if(SFML_BUILD_WINDOW)
add_subdirectory(window)
endif()
if(SFML_BUILD_GRAPHICS)
add_subdirectory(file_dropping)
add_subdirectory(opengl)
add_subdirectory(shader)
if(SFML_OS_WINDOWS)
Expand Down
10 changes: 10 additions & 0 deletions examples/file_dropping/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

set(SRCROOT ${PROJECT_SOURCE_DIR}/examples/file_dropping)

# all source files
set(SRC ${SRCROOT}/FileDropping.cpp)

# define the file_dropping target
sfml_add_example(file_dropping GUI_APP
SOURCES ${SRC}
DEPENDS sfml-graphics sfml-window sfml-system)
92 changes: 92 additions & 0 deletions examples/file_dropping/FileDropping.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <iostream>
#include <algorithm>

////////////////////////////////////////////////////////////
/// Entry point of application
///
/// \return Application exit code
///
////////////////////////////////////////////////////////////
int main()
{
// Create the main window
sf::RenderWindow window(sf::VideoMode(640, 480), "Drop image files here!");

// If the platform doesn't support file dropping, abort
if (!window.setFileDroppingEnabled(true))
{
std::cerr << "Platform not supported!\n";
return EXIT_FAILURE;
}

// The texture for displaying dropped images
sf::Texture texture;
texture.setSmooth(true);

// The sprite to draw the texture with
sf::Sprite sprite;
sprite.setTexture(texture);

// Start the game loop
while (window.isOpen())
{
// If this flag is set, we have to realign the view and the sprite
bool realign = false;

// Process events
sf::Event event;
while (window.pollEvent(event))
{
// Close event: close the window
if (event.type == sf::Event::Closed)
window.close();

// Resize event: request realignment
if (event.type == sf::Event::Resized)
realign = true;

// Files dropped event: try to load as image, set the title, and request realignment
if (event.type == sf::Event::FilesDropped)
{
for (unsigned int i = 0; i < event.droppedFiles.count; ++i)
{
if (texture.loadFromFile(event.droppedFiles.files[i]))
{
window.setTitle(event.droppedFiles.files[i]);
realign = true;
break;
}
}
}
}

// Align the sprite tidily into the center of the window and reset the view
if (realign)
{
window.setView(sf::View(sf::FloatRect(0.f, 0.f, static_cast<float>(window.getSize().x), static_cast<float>(window.getSize().y))));
sprite.setTextureRect(sf::IntRect(0, 0, texture.getSize().x, texture.getSize().y));
sprite.setOrigin(texture.getSize().x / 2.f, texture.getSize().y / 2.f);
sprite.setPosition(window.getSize().x / 2.f, window.getSize().y / 2.f);
float scalingFactor = 1.f;
if (texture.getSize().x > window.getSize().x || texture.getSize().y > window.getSize().y)
{
scalingFactor = std::min(
static_cast<float>(window.getSize().x) / static_cast<float>(texture.getSize().x),
static_cast<float>(window.getSize().y) / static_cast<float>(texture.getSize().y));
}
sprite.setScale(scalingFactor, scalingFactor);
}

// Display the image
window.clear();
window.draw(sprite);
window.display();
}

return EXIT_SUCCESS;
}
15 changes: 15 additions & 0 deletions include/SFML/Window/Event.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <SFML/Window/Keyboard.hpp>
#include <SFML/Window/Mouse.hpp>
#include <SFML/Window/Sensor.hpp>
#include <SFML/System/String.hpp>


namespace sf
Expand Down Expand Up @@ -180,6 +181,18 @@ class Event
float z; ///< Current value of the sensor on Z axis
};

////////////////////////////////////////////////////////////
/// \brief File dropping parameters (FilesDropped)
///
////////////////////////////////////////////////////////////
struct FileDropEvent
{
unsigned int count; ///< Number of dropped files
const String* files; ///< Pointer to the first file, access with event.droppedFiles.files[i] (in range [0 .. event.droppedFiles.count - 1])

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather see a std::vector<string> here, which would also make count obsolete.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the point above:

sf::Event::FileDropEvent is inevitably part of a union, so I couldn't use a vector but had to resort to a raw pointer. ...

@Fytch Fytch Apr 30, 2017 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All event data is stored in a union, so using std::vector<string> is not possible. Since C++11, it would theoretically be allowed using placement new and manual destructor invocations. Alternatively, there's also std::variant/boost::variant (which is essentially a wrapper of the method described).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right, the destructor getting broken data. Damn. :(

int x; ///< X position of the mouse pointer, relative to the left of the owner window
int y; ///< Y position of the mouse pointer, relative to the top of the owner window
};

////////////////////////////////////////////////////////////
/// \brief Enumeration of the different types of events
///
Expand Down Expand Up @@ -209,6 +222,7 @@ class Event
TouchMoved, ///< A touch moved (data in event.touch)
TouchEnded, ///< A touch event ended (data in event.touch)
SensorChanged, ///< A sensor value changed (data in event.sensor)
FilesDropped, ///< Files have been dragged and dropped into the window (data in event.droppedFiles)

Count ///< Keep last -- the total number of event types
};
Expand All @@ -232,6 +246,7 @@ class Event
JoystickConnectEvent joystickConnect; ///< Joystick (dis)connect event parameters (Event::JoystickConnected, Event::JoystickDisconnected)
TouchEvent touch; ///< Touch events parameters (Event::TouchBegan, Event::TouchMoved, Event::TouchEnded)
SensorEvent sensor; ///< Sensor event parameters (Event::SensorChanged)
FileDropEvent droppedFiles; ///< File dropping parameters (Event::FilesDropped)
};
};

Expand Down
16 changes: 16 additions & 0 deletions include/SFML/Window/Window.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,22 @@ class SFML_WINDOW_API Window : GlResource, NonCopyable
////////////////////////////////////////////////////////////
void setJoystickThreshold(float threshold);

////////////////////////////////////////////////////////////
/// \brief Enable or disable file dropping
///
/// If file dropping is enabled, you will receive FilesDropped
/// events whenever the user drags and drops a file into the
/// window.
///
/// File dropping is disabled by default.
///
/// \param enabled True to enable, false to disable
///
/// \return True if operation was successful, false otherwise
///
////////////////////////////////////////////////////////////
bool setFileDroppingEnabled(bool enabled);

////////////////////////////////////////////////////////////
/// \brief Activate or deactivate the window as the current target
/// for OpenGL rendering
Expand Down
52 changes: 51 additions & 1 deletion src/SFML/Window/Win32/WindowImplWin32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ m_cursorGrabbed (m_fullscreen)

++handleCount;
}

// By default, the OS limits the size of the window the the desktop size,
// we have to resize it after creation to apply the real size
setSize(Vector2u(mode.width, mode.height));
Expand Down Expand Up @@ -413,6 +413,13 @@ void WindowImplWin32::setKeyRepeatEnabled(bool enabled)
}


////////////////////////////////////////////////////////////
bool WindowImplWin32::setFileDroppingEnabled(bool enabled)
{
DragAcceptFiles(m_handle, enabled);
return true;
}

////////////////////////////////////////////////////////////
void WindowImplWin32::requestFocus()
{
Expand Down Expand Up @@ -972,6 +979,49 @@ void WindowImplWin32::processEvent(UINT message, WPARAM wParam, LPARAM lParam)
JoystickImpl::updateConnections();
break;
}

// File drop event
case WM_DROPFILES:
{
const HDROP hDrop = (HDROP)wParam;

// Generate a FilesDropped event
Event event;
event.type = Event::FilesDropped;

// Get the count
event.droppedFiles.count = DragQueryFileW(hDrop, 0xFFFFFFFF, NULL, 0);
if (event.droppedFiles.count == 0)
break;

// Expand the buffer list by one vector
FilesType& files = *m_droppedFiles.insert(m_droppedFiles.end(), FilesType());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't make any sense either. First you append an element, then resize? Or did I miss something? And why the reference?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m_droppedFiles is a std::list of std::vectors of sf::Strings. First append to the list, then resize the newly appended vector.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah,, right, should probably read it twice next time. So you're appending a new list for every drag&drop operation. Too bad we can't free them unless we assume that it's only valid till the next event is polled.

@Fytch Fytch Apr 30, 2017 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly. I append a new vector to the list for every drop operation. I decided this way because I would rather waste some Kilobytes than get nasty SIGSEGVs. User friendliness is more important to me.


// Resize and fill said buffer vector
files.resize(event.droppedFiles.count);
for (unsigned int i = 0; i < event.droppedFiles.count; ++i)
{
std::vector<wchar_t> buf;
buf.resize(DragQueryFileW(hDrop, i, NULL, 0) + 1);
DragQueryFileW(hDrop, i, &buf.front(), buf.size());
files[i] = &buf.front();
}

// Let the event point to the buffer vector
event.droppedFiles.files = &files.front();

// Fetch the cursor's window-relative position
POINT point;
DragQueryPoint(hDrop, &point);
event.droppedFiles.x = static_cast<int>(point.x);
event.droppedFiles.y = static_cast<int>(point.y);

// Tell the WinAPI to release the internal buffer
DragFinish(hDrop);

pushEvent(event);
break;
}
}
}

Expand Down
40 changes: 29 additions & 11 deletions src/SFML/Window/Win32/WindowImplWin32.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
#include <SFML/Window/WindowImpl.hpp>
#include <SFML/System/String.hpp>
#include <windows.h>
#include <vector>
#include <list>


namespace sf
Expand Down Expand Up @@ -161,6 +163,16 @@ class WindowImplWin32 : public WindowImpl
////////////////////////////////////////////////////////////
virtual void setKeyRepeatEnabled(bool enabled);

////////////////////////////////////////////////////////////
/// \brief Enable or disable file dropping
///
/// \param enabled True to enable, false to disable
///
/// \return True if operation was successful, false otherwise
///
////////////////////////////////////////////////////////////
virtual bool setFileDroppingEnabled(bool enabled);

////////////////////////////////////////////////////////////
/// \brief Request the current window to be made the active
/// foreground window
Expand Down Expand Up @@ -261,20 +273,26 @@ class WindowImplWin32 : public WindowImpl
////////////////////////////////////////////////////////////
static LRESULT CALLBACK globalOnEvent(HWND handle, UINT message, WPARAM wParam, LPARAM lParam);

////////////////////////////////////////////////////////////
// Type for storing a batch of dropped files
////////////////////////////////////////////////////////////
typedef std::vector<String> FilesType;

////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
HWND m_handle; ///< Win32 handle of the window
LONG_PTR m_callback; ///< Stores the original event callback function of the control
HCURSOR m_cursor; ///< The system cursor to display into the window
HICON m_icon; ///< Custom icon assigned to the window
bool m_keyRepeatEnabled; ///< Automatic key-repeat state for keydown events
Vector2u m_lastSize; ///< The last handled size of the window
bool m_resizing; ///< Is the window being resized?
Uint16 m_surrogate; ///< First half of the surrogate pair, in case we're receiving a Unicode character in two events
bool m_mouseInside; ///< Mouse is inside the window?
bool m_fullscreen; ///< Is the window fullscreen?
bool m_cursorGrabbed; ///< Is the mouse cursor trapped?
HWND m_handle; ///< Win32 handle of the window
LONG_PTR m_callback; ///< Stores the original event callback function of the control
HCURSOR m_cursor; ///< The system cursor to display into the window
HICON m_icon; ///< Custom icon assigned to the window
bool m_keyRepeatEnabled; ///< Automatic key-repeat state for keydown events
Vector2u m_lastSize; ///< The last handled size of the window
bool m_resizing; ///< Is the window being resized?
Uint16 m_surrogate; ///< First half of the surrogate pair, in case we're receiving a Unicode character in two events
bool m_mouseInside; ///< Mouse is inside the window?
bool m_fullscreen; ///< Is the window fullscreen?
bool m_cursorGrabbed; ///< Is the mouse cursor trapped?
std::list<FilesType> m_droppedFiles; ///< Buffer for the dropped files

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this member even needed? The list of files is in the event after all.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the event only stores a pointer to the buffer. See the first two bullet points in my issues list.

};

} // namespace priv
Expand Down
9 changes: 9 additions & 0 deletions src/SFML/Window/Window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,14 @@ void Window::setJoystickThreshold(float threshold)
}


////////////////////////////////////////////////////////////
bool Window::setFileDroppingEnabled(bool enabled)
{
if (m_impl)
m_impl->setFileDroppingEnabled(enabled);
}


////////////////////////////////////////////////////////////
bool Window::setActive(bool active) const
{
Expand Down Expand Up @@ -424,6 +432,7 @@ void Window::initialize()
setVerticalSyncEnabled(false);
setKeyRepeatEnabled(true);
setFramerateLimit(0);
setFileDroppingEnabled(false);

// Get and cache the initial size of the window
m_size = m_impl->getSize();
Expand Down
10 changes: 10 additions & 0 deletions src/SFML/Window/WindowImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,16 @@ void WindowImpl::setJoystickThreshold(float threshold)
}


////////////////////////////////////////////////////////////
bool WindowImpl::setFileDroppingEnabled(bool enabled)
{
// This is the fallback for unsupported platforms.
// Trying to enable file dropping returns a bad value
// indicating an error, whereas disabling it is fine.
return !enabled;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is confusing. Why would it return !enabled? This indicates a success, if you fail to disable it.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, disabling a non-existent function is always possible. Only enabling it would be troublesome because it - well - doesn't exist.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, okay, from that perspective it makes sense. But I'm not 100% sure how it should react considering it's not implemented/available. You're making it fail silently.

@Fytch Fytch Apr 30, 2017 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion, it doesn't fail silently. Disabling it is indistinguishable from it not being implemented, so why generate an error?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I can live with that when disabling.

}


////////////////////////////////////////////////////////////
bool WindowImpl::popEvent(Event& event, bool block)
{
Expand Down
10 changes: 10 additions & 0 deletions src/SFML/Window/WindowImpl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,16 @@ class WindowImpl : NonCopyable
////////////////////////////////////////////////////////////
virtual void setKeyRepeatEnabled(bool enabled) = 0;

////////////////////////////////////////////////////////////
/// \brief Enable or disable file dropping
///
/// \param enabled True to enable, false to disable
///
/// \return True if operation was successful, false otherwise
///
////////////////////////////////////////////////////////////
virtual bool setFileDroppingEnabled(bool enabled);

////////////////////////////////////////////////////////////
/// \brief Request the current window to be made the active
/// foreground window
Expand Down