-
Notifications
You must be signed in to change notification settings - Fork 0
Source: GTK SFMLWidget
This class is used to integrate a SFML RenderWindow into a gtkmm userinterface. Just use it like a normal RenderWindow but place it as you would a gtkmm widget.
Just a simple example of a gtkmm interface that has a SFMLWidget and then a Gtk::Button below it.
#include <gtkmm.h>
#include "SFMLWidget.h"
int main(int argc, char* argv[])
{
Gtk::Main kit(argc, argv); //Initialize Gtk
Gtk::Window window; //The GTK window will be our top level Window
//Our RenderWindow will never be below 640x480 (unless we explicitly change it)
//but it may be more then that
SFMLWidget ourRenderWindow(sf::VideoMode(640, 480));
// Doesn't draw the renderWindow but makes it so it will draw when we add it to the window
ourRenderWindow.show();
//VBox is a vertical box, we're going to pack our render window and a button in here
Gtk::VBox ourVBox;
Gtk::Button ourButton("Hello I do nothing"); //Just a clickable button, it won't be doing anything
ourButton.show();
ourVBox.pack_start(ourRenderWindow); //Add ourRenderWindow to the top of the VBox
//PACK_SHRINK makes the VBox only allocate enough space to show the button and nothing more
ourVBox.pack_start(ourButton, Gtk::PACK_SHRINK);
ourVBox.show();
window.add(ourVBox); //Adds ourVBox to the window so it (and it's children) can be drawn
Gtk::Main::run(window); //Draw the window
return 0;
}
Note: This was compiled using G++ and gtkmm-3.0 on a machine running Linux Mint 12.04
#ifndef SFMLWIDGET_H_INCLUDED
#define SFMLWIDGET_H_INCLUDED
#include <SFML/Graphics.hpp>
#include <gtkmm/widget.h>
class SFMLWidget : public Gtk::Widget
{
protected:
sf::VideoMode m_vMode;
virtual void on_size_allocate(Gtk::Allocation& allocation);
virtual void on_realize();
virtual void on_unrealize();
Glib::RefPtr<Gdk::Window> m_refGdkWindow;
public:
sf::RenderWindow renderWindow;
SFMLWidget(sf::VideoMode mode, int size_request=-1);
virtual ~SFMLWidget();
void invalidate();
void display();
};
#endif
#include "SFMLWidget.h"
#include <iostream>
// Tested on Linux Mint 12.4 and Windows 7
#if defined(SFML_SYSTEM_WINDOWS)
#include <gdk/gdkwin32.h>
#define GET_WINDOW_HANDLE_FROM_GDK GDK_WINDOW_HANDLE
#elif defined(SFML_SYSTEM_LINUX) || defined(SFML_SYSTEM_FREEBSD)
#include <gdk/gdkx.h>
#define GET_WINDOW_HANDLE_FROM_GDK GDK_WINDOW_XID
#elif defined(SFML_SYSTEM_MACOS)
#error Note: You have to figure out an analogue way to access the handle of the widget on a Mac-System
#else
#error Unsupported Operating System
#endif
SFMLWidget::SFMLWidget(sf::VideoMode mode, int size_request)
{
if(size_request<=0)
size_request = std::max<int>(1, std::min<int>(mode.width, mode.height) / 2);
set_size_request(size_request, size_request);
set_has_window(false); // Makes this behave like an interal object rather then a parent window.
}
SFMLWidget::~SFMLWidget()
{
}
void SFMLWidget::on_size_allocate(Gtk::Allocation& allocation)
{
//Do something with the space that we have actually been given:
//(We will not be given heights or widths less than we have requested, though
//we might get more)
this->set_allocation(allocation);
if(m_refGdkWindow)
{
m_refGdkWindow->move_resize(allocation.get_x(),
allocation.get_y(),
allocation.get_width(),
allocation.get_height() );
renderWindow.setSize(sf::Vector2u(allocation.get_width(),
allocation.get_height()));
}
}
void SFMLWidget::on_realize()
{
Gtk::Widget::on_realize();
if(!m_refGdkWindow)
{
//Create the GdkWindow:
GdkWindowAttr attributes;
memset(&attributes, 0, sizeof(attributes));
Gtk::Allocation allocation = get_allocation();
//Set initial position and size of the Gdk::Window:
attributes.x = allocation.get_x();
attributes.y = allocation.get_y();
attributes.width = allocation.get_width();
attributes.height = allocation.get_height();
attributes.event_mask = get_events () | Gdk::EXPOSURE_MASK;
attributes.window_type = GDK_WINDOW_CHILD;
attributes.wclass = GDK_INPUT_OUTPUT;
m_refGdkWindow = Gdk::Window::create(get_window(), &attributes,
GDK_WA_X | GDK_WA_Y);
set_has_window(true);
set_window(m_refGdkWindow);
// transparent background
#if GTK_VERSION_GE(3, 0)
this->unset_background_color();
#else
this->get_window()->set_back_pixmap(Glib::RefPtr<Gdk::Pixmap>());
#endif
this->set_double_buffered(false);
//make the widget receive expose events
m_refGdkWindow->set_user_data(gobj());
renderWindow.create(GET_WINDOW_HANDLE_FROM_GDK(m_refGdkWindow->gobj()));
}
}
void SFMLWidget::on_unrealize()
{
m_refGdkWindow.clear();
//Call base class:
Gtk::Widget::on_unrealize();
}
void SFMLWidget::display()
{
if(m_refGdkWindow)
{
renderWindow.display();
}
}
void SFMLWidget::invalidate()
{
if(m_refGdkWindow)
{
m_refGdkWindow->invalidate(true);
}
}
In order to render something, you should overload the on_draw
method or connect to the signal_draw()
signal (in earlier version of gtkmm you should use signal_expose_event
instead). Don't forget to call the display()
method. Use SFMLWidget::display()
instead of sf::RenderWindow::display()
as it will check whether the Widget has been already initialized.
In order to add animate something, call invalidate()
often enough. This will cause gtkmm to redraw the Widget.
The following example shows one way to animate and render a circle.
Note: This was compiled using G++ and gtkmm-3.0 on a machine running Linux Mint 12.04
#include <gtkmm.h>
#include "SFMLWidget.h"
class MovingCircle
{
public:
// A simple circle shape that will be animated and drawn
sf::CircleShape circle;
// a reference to our SFMLWidget
SFMLWidget& widget;
// The radius of our circle
float radius;
MovingCircle(SFMLWidget& widget) : widget(widget)
{
// Set the radius to an unmiportand value
radius = 32.f;
circle.setRadius(radius);
// move the circoe to it's first position
moveToStartPoint();
// Let the animate method be called every 50ms
// Note: MovingCircle::animate() doesn't return any value, but signal_timeout() expects
// a boolean value.
// Using sigc::bind_return(true, ...) we get a signal returning always true.
Glib::signal_timeout().connect(sigc::bind_return(
sigc::mem_fun(this, &MovingCircle::animate),
true),
50);
// Makes our draw Method beeing drawn everytime the widget itself gets drawn.
// Note: MovingCircle::draw() doesn't accept any parameter, but signal_draw() gives one.
// Using sigc::hide(...) we get a signal expecting one.
widget.signal_draw().connect(sigc::bind_return(
sigc::hide(
sigc::mem_fun(this, &MovingCircle::draw)),
true));
// NOTE: in earlier gtkmm-versions (<3.0) instead of signal_draw, connext to signal_event_expose:
// widget.signal_event_expose().connect(sigc::bind_return(
// sigc::hide(
// sigc::mem_fun(this, &MovingCircle::draw)),
// true));
// Everytime the widget gets resized, we need to adjust the view.
widget.signal_size_allocate().connect(sigc::hide(
sigc::mem_fun(this, &MovingCircle::resize_view)));
}
void animate()
{
// Simply move the circle...
sf::Vector2f position = circle.getPosition();
position.x += 8.f;
position.y += 8.f;
// until it "leaves" the Widget
if(position.x > widget.renderWindow.getSize().x+radius || position.y > widget.renderWindow.getSize().y+radius)
moveToStartPoint();
else
circle.setPosition(position);
// Tell gtkmm that the SFML Widget wants to be redrawn
widget.invalidate();
}
void draw()
{
widget.renderWindow.clear();
widget.renderWindow.draw(circle);
// Calls SFMLWidget::display, whitch checks wether the widget is realized
// and if so, sf::RenderWindow::display gets called.
widget.display();
}
void resize_view()
{
// Let the View fit the pixels of the window.
sf::Vector2f lower_right(widget.renderWindow.getSize().x,
widget.renderWindow.getSize().y);
sf::View view(lower_right * 0.5f,
lower_right);
widget.renderWindow.setView(view);
}
void moveToStartPoint()
{
circle.setPosition(-radius, -radius);
}
};
int main(int argc, char* argv[])
{
Gtk::Main kit(argc, argv); //Initialize Gtk
Gtk::Window window; //The GTK window will be our top level Window
//Our RenderWindow will never be below 640x480 (unless we explicitly change it)
//but it may be more then that
SFMLWidget ourRenderWindow(sf::VideoMode(640, 480));
MovingCircle moving_circle(ourRenderWindow);
// Doesn't draw the renderWindow but makes it so it will draw when we add it to the window
ourRenderWindow.show();
//VBox is a vertical box, we're going to pack our render window and a button in here
Gtk::VBox ourVBox;
Gtk::Button ourButton("Restart"); //Just a clickable button, it won't be doing anything
ourButton.show();
ourButton.signal_clicked().connect(sigc::mem_fun(&moving_circle, &MovingCircle::moveToStartPoint));
ourVBox.pack_start(ourRenderWindow); //Add ourRenderWindow to the top of the VBox
//PACK_SHRINK makes the VBox only allocate enough space to show the button and nothing more
ourVBox.pack_start(ourButton, Gtk::PACK_SHRINK);
ourVBox.show();
window.add(ourVBox); //Adds ourVBox to the window so it (and it's children) can be drawn
Gtk::Main::run(window); //Draw the window
return 0;
}