The Framework
Table of Contents
UI Framework Setup
The central classes for handling windows are the DisplayManager and the Window class. The former contains a list of all active windows spawned by the program and starts a thread in which they run upon initialization.
Basically, in any new project we start by extending the DisplayManager class with a class I always called DisplayInstance, this child class has all the global variables and controls all the windows spawned by the program.
#pragma once
#include "../../Core/Timer/Timer.h"
#include "../../Core/DynamicArray/DynamicArray.h"
#include "../ThreadManager/ThreadManager.h"
#include "../Window/Window.h"
class DisplayManager : ThreadManager<DisplayManager> {
friend ThreadManager<DisplayManager>;
public:
DisplayManager();
bool Loading();
Window *SpawnWindow(WindowArgs winArgs = WindowArgs());
bool NoWindowOpen();
void Close();
private:
void ThreadMain(); //loop function
void ErrorHandling(WindowInitialisationErrorCodes code);
static LRESULT CALLBACK MessageRouter
(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
core::Timer _timer;
core::DynamicArray<Window *> _windowList;
bool _adding;
bool _close;
};core::DynamicArray is my own linked list implementation and could be replaced by any list class, e.g. std::vector. ThreadManager is a std::thread wrapper class:
#pragma once
#include <thread>
template <typename T>
class ThreadManager {
protected:
ThreadManager() {}
void Start();
void Stop();
private:
std::thread _mainThread;
};
template<class T>
inline void ThreadManager<T>::Start() {
_mainThread = std::thread(&T::ThreadMain, static_cast<T*>(this));
}
template<class T>
inline void ThreadManager<T>::Stop() {
if (_mainThread.joinable())
_mainThread.join();
}When we call the function DisplayManager::Start the function DisplayManager::MainThread is called in a separate thread. To create a window we call the DisplayManager::SpawnWindow function, which adds a window to _windowList. The MainThread function will then spawn the window in its next loop:
void DisplayManager::ThreadMain() {
// run at 60fps
_timer = core::Timer(60);
MSG msg;
msg.message = WM_NULL;
while (!_close) {
for (int i = _windowList.size() - 1; i > -1 && !_adding; i--) {
// initialize new windows
if(!_windowList[i]->_isInitialised) {
ErrorHandling(_windowList[i]->initialise(MessageRouter));
continue;
}
// remove closed windows for the loop
if (!IsWindow(_windowList[i]->_winHandle)) {
_windowList.erase(_windowList.begin() + i);
break;
}
// draw the windows
if (_timer.tick()
&& _windowList[i]->_args->WindowSize != core::Size(0,0))
_windowList[i]->draw();
// handle closing windows
if (_windowList[i]->_isClosing)
PostMessage(_windowList[i]->_winHandle, WM_CLOSE, 0, 0);
// event handling
#ifdef USAGE_TYPE_GAME
if (PeekMessage(&msg, _windowList[i]->_winHandle, 0, 0, PM_REMOVE))
DispatchMessage(&msg);
#else
if (GetMessage(&msg, _windowList[i]->_winHandle, 0, 0))
DispatchMessage(&msg);
#endif
}
}
while (!_windowList.empty()) {
for (int i = _windowList.size() - 1; i > -1 && !_adding; i--) {
PostMessage(_windowList[i]->_winHandle, WM_CLOSE, 0, 0);
if (GetMessage(&msg, _windowList[i]->_winHandle, 0, 0))
DispatchMessage(&msg);
if (!IsWindow(_windowList[i]->_winHandle)) {
_windowList.erase(_windowList.begin() + i);
break;
}
}
}
}