Events explained#
Introduction#
This tutorial is a detailed list of window events. It describes them, and shows how to (and how not to) use them.
The SF::Event type#
Before dealing with events, it is important to understand what the SF::Event type is, and how to correctly use it. SF::Event (unlike in SFML, where it is a union) is just an abstract struct, and all the events are its subclasses. Many events have some data associated with them, so it is important to let the compiler know which exactly type of event is being inspected (using is_a?
, as
, case
/when
), otherwise none of the members will be accessible.
Here is the hierarchy: level 1 is the Event
abstract struct itself, level 2 are abstract structs that add some members, and level 3 are concrete event types. The point of this is that that some events, while different (level 3), have exactly the same kind of information associated with them (level 2).
Event
├────╴Closed
├─╴SizeEvent: height, width
│ └─╴Resized
├────╴LostFocus
├────╴GainedFocus
├─╴TextEvent: unicode
│ └─╴TextEntered
├─╴KeyEvent: code, alt, control, shift, system
│ ├─╴KeyPressed
│ └─╴KeyReleased
├─╴MouseWheelEvent: delta, x, y
│ └─╴MouseWheelMoved
├─╴MouseWheelScrollEvent: wheel, delta, x, y
│ └─╴MouseWheelScrolled
├─╴MouseButtonEvent: button, x, y
│ ├─╴MouseButtonPressed
│ └─╴MouseButtonReleased
├─╴MouseMoveEvent: x, y
│ └─╴MouseMoved
├────╴MouseEntered
├────╴MouseLeft
├─╴JoystickButtonEvent: joystick_id, button
│ ├─╴JoystickButtonPressed
│ └─╴JoystickButtonReleased
├─╴JoystickMoveEvent: joystick_id, axis, position
│ └─╴JoystickMoved
├─╴JoystickConnectEvent: joystick_id
│ ├─╴JoystickConnected
│ └─╴JoystickDisconnected
├─╴TouchEvent: finger, x, y
│ ├─╴TouchBegan
│ ├─╴TouchMoved
│ └─╴TouchEnded
└─╴SensorEvent: type, x, y, z
└─╴SensorChanged
SF::Event instances are filled by the poll_event
(or wait_event
) method of the SF::Window class. Only these two methods can produce valid events.
To be clear, here is what a typical event loop looks like:
# while there are pending events...
while event = window.poll_event
# check the type of the event...
case event
when SF::Event::Closed # window closed
window.close
when SF::Event::KeyPressed # key pressed
# [...]
end # we don't process other types of events
end
Alright, now we can see what events SFML supports, what they mean and how to use them properly.
The Closed event#
Relevant example: simple
The SF::Event::Closed
event is triggered when the user wants to close the window, through any of the possible methods the window manager provides ("close" button, keyboard shortcut, etc.). This event only represents a close request, the window is not yet closed when the event is received.
Typical code will just call window.close
in reaction to this event, to actually close the window. However, you may also want to do something else first, like saving the current application state or asking the user what to do. If you don't do anything, the window remains open.
There's no member associated with this event in the SF::Event union.
if event.is_a? SF::Event::Closed
window.close
end
The Resized event#
Relevant example: gl
The SF::Event::Resized
event is triggered when the window is resized, either through user action or programmatically by calling window.size=
.
You can use this event to adjust the rendering settings: the viewport if you use OpenGL directly, or the current view if you use sfml-graphics.
The data associated with this event is the new size of the window.
if event.is_a? SF::Event::Resized
puts "new width: #{event.width}"
puts "new height: #{event.height}"
end
The LostFocus and GainedFocus events#
The SF::Event::LostFocus
and SF::Event::GainedFocus
events are triggered when the window loses/gains focus, which happens when the user switches the currently active window. When the window is out of focus, it doesn't receive keyboard events.
This event can be used e.g. if you want to pause your game when the window is inactive.
There's no member associated with these events in the SF::Event union.
if event.is_a? SF::Event::LostFocus
my_game.pause
end
if event.is_a? SF::Event::GainedFocus
my_game.resume
end
The TextEntered event#
Relevant example: typing
The SF::Event::TextEntered
event is triggered when a character is typed. This must not be confused with the KeyPressed
event: TextEntered
interprets the user input and produces the appropriate printable character. For example, pressing '^' then 'e' on a French keyboard will produce two KeyPressed
events, but a single TextEntered
event containing the 'ê' character. It works with all the input methods provided by the operating system, even the most specific or complex ones.
This event is typically used to catch user input in a text field.
The data associated with this event is the Unicode codepoint of the entered character (use .chr
to convert it to a Char
).
if event.is_a? SF::Event::TextEntered
if event.unicode < 128
puts "ASCII character typed: #{event.unicode.chr}"
end
end
Note that, since they are part of the Unicode standard, some non-printable characters such as backspace are generated by this event. In most cases you'll need to filter them out.
Many programmers use the KeyPressed
event to get user input, and start to implement crazy algorithms that try to interpret all the possible key combinations to produce correct characters. Don't do that!
The KeyPressed and KeyReleased events#
Relevant example: snakes
The SF::Event::KeyPressed
and SF::Event::KeyReleased
events are triggered when a keyboard key is pressed/released.
If a key is held, multiple KeyPressed
events will be generated, at the default operating system delay (i. e. the same delay that applies when you hold a letter in a text editor). To disable repeated KeyPressed
events, you can set window.key_repeat_enabled = false
. On the flip side, it is obvious that KeyReleased
events can never be repeated.
This event is the one to use if you want to trigger an action exactly once when a key is pressed or released, like making a character jump with space, or exiting something with escape.
Sometimes, people try to react to KeyPressed
events directly to implement smooth movement. Doing so will not produce the expected effect, because when you hold a key you only get a few events (remember, the repeat delay). To achieve smooth movement with events, you must use a boolean that you set on KeyPressed
and clear on KeyReleased
; you can then move (independently of events) as long as the boolean is set.
The other (easier) solution to produce smooth movement is to use real-time keyboard input with SF::Keyboard (see the dedicated tutorial).
The data associated with these events is the code of the pressed/released key, as well as the current state of the modifier keys (alt, control, shift, system).
if event.is_a? SF::Event::KeyPressed
if event.code == SF::Keyboard::Escape
puts "the escape key was pressed"
puts "control: #{event.control}"
puts "alt: #{event.alt}"
puts "shift: #{event.shift}"
puts "system: #{event.system}"
end
end
Note that some keys have a special meaning for the operating system, and will lead to unexpected behavior. An example is the F10 key on Windows, which "steals" the focus, or the F12 key which starts the debugger when using Visual Studio. This will probably be solved in a future version of SFML.
The MouseWheelMoved event#
The SF::Event::MouseWheelMoved
event is deprecated since SFML 2.3, use the MouseWheelScrolled event instead.
The MouseWheelScrolled event#
Relevant example: diagnostics
The SF::Event::MouseWheelScrolled
event is triggered when a mouse wheel moves up or down, but also laterally if the mouse supports it.
The data associated with this event contains the number of ticks the wheel has moved, what the orientation of the wheel is and the current position of the mouse cursor.
if event.is_a? SF::Event::MouseWheelScrolled
if event.wheel == SF::Mouse::VerticalWheel
puts "wheel type: vertical"
elsif event.wheel == SF::Mouse::HorizontalWheel
puts "wheel type: horizontal"
else
puts "wheel type: unknown"
end
puts "wheel movement: #{event.delta}"
puts "mouse x: #{event.x}"
puts "mouse y: #{event.y}"
end
The MouseButtonPressed and MouseButtonReleased events#
Relevant example: diagnostics
The SF::Event::MouseButtonPressed
and SF::Event::MouseButtonReleased
events are triggered when a mouse button is pressed/released.
SFML supports 5 mouse buttons: left, right, middle (wheel), extra #1 and extra #2 (side buttons).
The data associated with these events contains the code of the pressed/released button, as well as the current position of the mouse cursor.
if event.is_a? SF::Event::MouseButtonPressed
if event.button.right?
puts "the right button was pressed"
puts "mouse x: #{event.x}"
puts "mouse y: #{event.y}"
end
end
The MouseMoved event#
The SF::Event::MouseMoved
event is triggered when the mouse moves within the window.
This event is triggered even if the window isn't focused. However, it is triggered only when the mouse moves within the inner area of the window, not when it moves over the title bar or borders.
The data associated with this event contains the current position of the mouse cursor relative to the window.
if event.is_a? SF::Event::MouseMoved
puts "new mouse x: #{event.x}"
puts "new mouse y: #{event.y}"
end
The MouseEntered and MouseLeft event#
The SF::Event::MouseEntered
and SF::Event::MouseLeft
events are triggered when the mouse cursor enters/leaves the window.
There is no data associated with these events.
case event
when SF::Event::MouseEntered
puts "the mouse cursor has entered the window"
when SF::Event::MouseLeft
puts "the mouse cursor has left the window"
end
The JoystickButtonPressed and JoystickButtonReleased events#
The SF::Event::JoystickButtonPressed
and SF::Event::JoystickButtonReleased
events are triggered when a joystick button is pressed/released.
SFML supports up to 8 joysticks and 32 buttons.
The data associated with these events contains the identifier of the joystick and the index of the pressed/released button.
if event.is_a? SF::Event::JoystickButtonPressed
puts "joystick button pressed!"
puts "joystick id: #{event.joystick_id}"
puts "button: #{event.button}"
end
The JoystickMoved event#
The SF::Event::JoystickMoved
event is triggered when a joystick axis moves.
Joystick axes are typically very sensitive, that's why SFML uses a detection threshold to avoid spamming your event loop with tons of JoystickMoved
events. This threshold can be changed with the Window#joystick_threshold=
method, in case you want to receive more or less joystick move events.
SFML supports 8 joystick axes: X, Y, Z, R, U, V, POV X and POV Y. How they map to your joystick depends on its driver.
The member associated with this event contains the identifier of the joystick, the name of the axis, and its current position (in the range [-100, 100]).
if event.is_a? SF::Event::JoystickMoved
if event.axis == SF::Joystick::X
puts "X axis moved!"
puts "joystick id: #{event.joystick_id}"
puts "new position: #{event.position}"
end
end
The JoystickConnected and JoystickDisconnected events#
The SF::Event::JoystickConnected
and SF::Event::JoystickDisconnected
events are triggered when a joystick is connected/disconnected.
The data associated with this event is the identifier of the connected/disconnected joystick.
case event
when SF::Event::JoystickConnected
puts "joystick connected: #{event.joystick_id}"
when SF::Event::JoystickDisconnected
puts "joystick disconnected: #{event.joystick_id}"
end