Mine looks like this:
ApplicationWindow.h
#ifndef ENGINE_PLATFORM_APPLICATIONWINDOW_H
#define ENGINE_PLATFORM_APPLICATIONWINDOW_H
#include <SFML/Graphics.hpp>
#include <QtWidgets/QWidget>
#include "Engine/Assorted/MeasurementUnits.h"
#include "Common/Types/WindowSize.h"
#include "Common/System/Macroes/BuildNote.h"
DEPENDS_ON_QT;
DEPENDS_ON_SFML;
//Warning: The sf::RenderWindow is not created until after construction, when the first showEvent() occures.
//This means any loading of SFML resources like images must take place *after* the first showEvent() of AreaWindow.
BUILD_NOTE("Should sf::RenderWindow be privately inheritted, instead of publicly?")
//This is the window that actually contains the game itself.
//Draw to it using SFML, using the sf::RenderTarget provided by ApplicationStructure::onDraw().
class ApplicationWindow : public QWidget, public sf::RenderWindow
{
Q_OBJECT
public:
ApplicationWindow(QWidget *parent = nullptr);
~ApplicationWindow();
//Manually set the size of the QWidget, forcing it to be 'size'.
void SetWindowSize(WindowSize size);
//Manually set the virtual size. (If set too high, more AreaCells will display then is currently loaded)
void SetVirtualSize(VirtualWindowSize size);
//Resets the camera size. Called automaticly when the virtual size changes.
void ResetCamera();
WindowSize GetWindowSize();
VirtualWindowSize GetVirtualSize();
//Sets the center position of the camera in the world. Do so each frame before drawing the world.
void SetWorldCamera(VisiblePosInWorld cameraCenter);
//Changes the camera mode, in preperation for drawing.
void SetCameraMode(CameraMode cameraMode);
//Sets a custom camera view. You must keep track of 'customView' yourself, ownership is not transfered.
void SetCustomCamera(const sf::View &customView);
//Sets the zooming of the camera, when using CameraMode::World.
void SetWorldZoom(float amount);
float GetWorldZoom() const;
//Converts a pixel screen position in WindowSize to a position in the world itself.
//Note: Don't call "ConvertToPosInWorld(ConvertToVirtualPos(pos))", just call "ConvertToPosInWorld(pos)".
PosInWorld ConvertToPosInWorld(const PosInWindow &posInWindow) const;
PosInWorld ConvertToPosInWorld(const PosInWindow &posInWindow, const sf::View &cameraView) const;
//Converts a pixel screen position in WindowSize to the equivilent pixel position in VirtualWindowSize.
PosInVirtualWindow ConvertToVirtualPos(const PosInWindow &posInWindow);
//Converts from a pixel screen position to a global monitor position.
PosInMonitor ConvertToPosInMonitor(const PosInWindow &posInWindow);
//Keeps the window at a proper aspect ratio.
int heightForWidth(int width);
private:
virtual QPaintEngine *paintEngine() const;
//void focusInEvent(QFocusEvent *);
//void focusOutEvent(QFocusEvent *);
//void keyPressEvent(QKeyEvent *);
void enterEvent(QEvent *) override;
//void mousePressEvent(QMouseEvent *event) override;
void showEvent(QShowEvent*) override;
void paintEvent(QPaintEvent*) override;
void resizeEvent(QResizeEvent*) override;
private:
bool isInitialized; //True if the SFML window has been initialized on the QWidget.
float worldZoom; //The amount zoomed in by the worldView camera.
//sf::RenderWindow only stores a *pointer* to the sf::View, so we need to keep a local variable so it doesn't go out of scope.
//One is the view in the world (for world drawing), the other is the view of the screen (for GUI drawing).
sf::View worldView;
sf::View screenView;
WindowSize windowSize;
VirtualWindowSize virtualSize;
signals:
void Draw(sf::RenderTarget&);
};
ApplicationWindow.cpp
#include "ApplicationWindow.h"
#include <QtGui/QResizeEvent>
#include "Common/Logger/Log.h"
#include "Common/String/Conversion.h"
#include "Common/Logic/Property/PropertyMap.h"
ApplicationWindow::ApplicationWindow(QWidget *parent)
: QWidget(parent), isInitialized(false)
{
//Setup some states to allow direct rendering into the widget.
QWidget::setAttribute(Qt::WA_PaintOnScreen);
QWidget::setAttribute(Qt::WA_OpaquePaintEvent);
QWidget::setAttribute(Qt::WA_NoSystemBackground);
QWidget::setAttribute(Qt::WA_PaintUnclipped);
//Set strong focus to enable keyboard events to be received.
QWidget::setFocusPolicy(Qt::StrongFocus);
this->worldZoom = 1.0f;
//I forget why I have this commented out. I think it was preventing QLineEdits from recieving keystrokes.
//this->grabKeyboard();
}
ApplicationWindow::~ApplicationWindow()
{
}
void ApplicationWindow::SetWindowSize(WindowSize size)
{
this->windowSize = size;
Log::Message message(MSG_SOURCE("AreaWindow", Log::Severity::Log));
message << "Setting the window size to " << Log_HighlightCyan(this->windowSize.ToString())
<< ", with a ratio of " << Log_HighlightCyan(ResolutionRatio(this->windowSize)) << ".\n" << Log::FlushStream;
#warning I can't do this here. ApplicationWindow will-be/should-be resized by it's parent.
QWidget::resize(this->windowSize.ToQSize());
QWidget::setFixedSize(this->windowSize.ToQSize());
}
//Manually set the virtual size. (If set too high, more AreaCells will display then is currently loaded)
void ApplicationWindow::SetVirtualSize(VirtualWindowSize size)
{
this->virtualSize = size;
Log::Message message(MSG_SOURCE("AreaWindow", Log::Severity::Log));
message << "Setting the virtual window size to " << Log_HighlightCyan(this->virtualSize.ToString())
<< ", with a ratio of " << Log_HighlightCyan(ResolutionRatio(this->virtualSize)) << "." << Log::FlushStream;
float virtualWidth = float(this->virtualSize.width);
float virtualHeight = float(this->virtualSize.height);
this->worldView.setSize(virtualWidth, virtualHeight);
this->SetWorldZoom(this->worldZoom); //Re-apply the zoom.
this->screenView.setCenter((virtualWidth * 0.5f), (virtualHeight * 0.5f));
this->screenView.setSize(virtualWidth, virtualHeight);
}
WindowSize ApplicationWindow::GetWindowSize()
{
return this->windowSize;
}
VirtualWindowSize ApplicationWindow::GetVirtualSize()
{
return this->virtualSize;
}
//Sets the center position of the camera in the world. Do so each frame before drawing the world.
void ApplicationWindow::SetWorldCamera(VisiblePosInWorld cameraCenter)
{
//Sets the center of the view.
this->worldView.setCenter(float(cameraCenter.x), float(cameraCenter.y));
}
//Changes the camera mode, in preperation for drawing.
void ApplicationWindow::SetCameraMode(CameraMode cameraMode)
{
if(cameraMode == CameraMode::World)
{
sf::RenderWindow::setView(this->worldView);
}
else
{
sf::RenderWindow::setView(this->screenView);
}
}
//Sets a custom camera view. You must keep track of 'customView' yourself, ownership is not transfered.
void ApplicationWindow::SetCustomCamera(const sf::View &customView)
{
sf::RenderWindow::setView(customView);
}
//Sets the zooming of the camera, when using CameraMode::World.
void ApplicationWindow::SetWorldZoom(float amount)
{
BUILD_NOTE("Zoom out, zoom in using scroll-wheel")
BUILD_NOTE("Different levels: 100%, 200%, 300%, 400%, 500%")
this->worldZoom = amount;
float virtualWidth = static_cast<float>(this->virtualSize.width);
float virtualHeight = static_cast<float>(this->virtualSize.height);
this->worldView.setSize((virtualWidth * this->worldZoom),
(virtualHeight * this->worldZoom));
}
float ApplicationWindow::GetWorldZoom() const
{
return this->worldZoom;
}
//Converts a pixel screen position in WindowSize to a position in the world itself.
//Note: Don't call "ConvertToPosInWorld(ConvertToVirtualPos(pos))", just call "ConvertToPosInWorld(pos)".
PosInWorld ApplicationWindow::ConvertToPosInWorld(const PosInWindow &posInWindow, const sf::View &cameraView) const
{
PosInWorld posInWorld;
posInWorld.FromSfmlVector2f(this->mapPixelToCoords({posInWindow.x, posInWindow.y}, cameraView));
return posInWorld;
}
PosInWorld ApplicationWindow::ConvertToPosInWorld(const PosInWindow &posInWindow) const
{
return this->ConvertToPosInWorld(posInWindow, this->worldView);
}
//Converts a pixel position in WindowSize to the equivilent pixel position in VirtualWindowSize.
PosInVirtualWindow ApplicationWindow::ConvertToVirtualPos(const PosInWindow &posInWindow)
{
WindowSize windowSize = this->GetWindowSize();
VirtualWindowSize virtualSize = this->GetVirtualSize();
float horizontalMultiplyer = float(virtualSize.width) / float(windowSize.width);
float verticalMultiplyer = float(virtualSize.height) / float(windowSize.height);
return PosInVirtualWindow(int(float(posInWindow.x) * horizontalMultiplyer),
int(float(posInWindow.y) * verticalMultiplyer));
}
//Converts from a pixel screen position to a global monitor position.
PosInMonitor ApplicationWindow::ConvertToPosInMonitor(const PosInWindow &posInWindow)
{
PosInMonitor posInMonitor;
posInMonitor.FromQPoint(QWidget::mapToGlobal(posInWindow.ToQPoint()));
return posInMonitor;
}
int ApplicationWindow::heightForWidth(int width)
{
float aspectRatio = float(this->windowSize.width) / float(this->windowSize.height);
int height = int(float(width) * aspectRatio);
return height;
}
QPaintEngine *ApplicationWindow::paintEngine() const
{
//We make the paintEvent function return a null paint engine. This functions works together with
//the WA_PaintOnScreen flag to tell Qt that we're not using any of its built-in paint engines.
return nullptr;
}
/*void ApplicationWindow::focusInEvent(QFocusEvent *)
{
std::cout << "QWidget::focusInEvent()" << std::endl;
}
void ApplicationWindow::focusOutEvent(QFocusEvent *)
{
std::cout << "QWidget::focusOutEvent()" << std::endl;
}
void ApplicationWindow::keyPressEvent(QKeyEvent *)
{
this->worldZoom = 1.0f;
}
void ApplicationWindow::mouseMoveEvent(QMouseEvent *event)
{
}*/
void ApplicationWindow::enterEvent(QEvent *)
{
BUILD_NOTE("This will accidentally take focus away from the TileSelectionWindow search box")
//Make sure the window takes focus the second the mouse goes over it.
QWidget::setFocus();
}
/*void ApplicationWindow::mousePressEvent(QMouseEvent *event)
{
}*/
void ApplicationWindow::showEvent(QShowEvent*)
{
if(!this->isInitialized)
{
//Under X11, we need to flush the commands sent to the server to ensure that
//SFML will get an updated view of the windows
#ifdef Q_WS_X11
XFlush(QX11Info::display());
#endif
//Create the SFML window with the widget handle
sf::RenderWindow::create((HWND)this->winId());
//Assigns our view to the window. The SFML window stores a pointer to it, so we can just update the view and the window will automaticly know.
sf::RenderWindow::setView(this->worldView);
this->isInitialized = true;
}
}
void ApplicationWindow::paintEvent(QPaintEvent *event)
{
//Clear the window.
const cColor &windowClearColor = GlobalProperties.Get<cColor>("WindowClearColor");
sf::RenderWindow::clear(windowClearColor.ToSfmlColor());
//Start the camera off on World mode.
this->SetCameraMode(CameraMode::World);
//Let the game know we are ready to draw, and give it the sf::RenderTarget.
emit Draw(*this);
//Display on screen.
sf::RenderWindow::display();
}
void ApplicationWindow::resizeEvent(QResizeEvent *resizeEvent)
{
return;
//Resize the SFML window to the new widget size.
//sf::RenderWindow::setSize({resizeEvent->size().width(), resizeEvent->size().height()});
}
I wrote it many months ago, so I forget why I had some parts commented out and why I did things the way I did. I'm going to have to revisit it in a week or two though, to get resizing handled properly.