Skip to content

Instantly share code, notes, and snippets.

@McMartin
Last active February 13, 2018 09:23
Show Gist options
  • Save McMartin/f823d0a91c5771de4c640914faaee570 to your computer and use it in GitHub Desktop.
Save McMartin/f823d0a91c5771de4c640914faaee570 to your computer and use it in GitHub Desktop.
#include "QtEventLoop.hpp"
#include <QApplication>
#include <QDebug>
#include <QKeyEvent>
#include <QLabel>
#include <QMainWindow>
#include <functional>
// Model -- BEGIN
struct model
{
int value = 0;
};
using action = char;
model update(model m, action a)
{
switch (a)
{
case '+':
return {m.value + 1};
case '-':
return {m.value - 1};
case '.':
return {0};
}
return m;
}
// Model -- END
// Example specific stuff -- BEGIN
void draw(QLabel* label, model m)
{
label->setText(QString::number(m.value));
}
action intent(const QEvent* ev)
{
if (ev->type() == QEvent::KeyPress)
{
auto ke = static_cast<const QKeyEvent*>(ev);
if (ke->text() == "+")
{
return '+';
}
if (ke->text() == "-")
{
return '-';
}
if (ke->text() == ".")
{
return '.';
}
}
return 0;
}
// Example specific stuff -- END
struct store
{
store(model init,
std::function<model(model, action)> update_fn,
std::function<void(model)> view_fn,
QtEventLoopWrapper loop)
: m_model(std::move(init))
, m_update_fn(std::move(update_fn))
, m_view_fn(std::move(view_fn))
, m_loop(std::move(loop))
{
m_loop.post([=] { m_view_fn(m_model); });
}
void dispatch(action a)
{
m_loop.post([=] {
m_model = m_update_fn(m_model, a);
m_view_fn(m_model);
});
}
private:
model m_model;
std::function<model(model, action)> m_update_fn;
std::function<void(model)> m_view_fn;
QtEventLoopWrapper m_loop;
};
QEvent::Type PostEvent::sEventType =
static_cast<QEvent::Type>(QEvent::registerEventType());
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
QMainWindow w;
auto label = new QLabel(&w);
w.show();
QtEventLoop loop;
auto s =
store{model{}, update, [=](model m) { draw(label, m); }, QtEventLoopWrapper{loop}};
loop.setHandler([&s](const QEvent* ev) {
if (auto a = intent(ev))
{
s.dispatch(a);
return true;
}
return false;
});
app.installEventFilter(&loop);
return app.exec();
}
#pragma once
#include <QCoreApplication>
#include <QEvent>
#include <QObject>
#include <functional>
class PostEvent : public QEvent
{
public:
static Type sEventType;
PostEvent::PostEvent(std::function<void()> f)
: QEvent(sEventType)
, m_f(std::move(f))
{
}
std::function<void()> m_f;
};
class QtEventLoop : public QObject
{
Q_OBJECT
public:
explicit QtEventLoop(QObject* parent = nullptr)
: QObject(parent)
{
}
void post(std::function<void()> f)
{
auto ev = new PostEvent(std::move(f));
QCoreApplication::postEvent(this, ev);
}
bool event(QEvent* ev) override
{
if (ev->type() == PostEvent::sEventType)
{
auto customEv = static_cast<PostEvent*>(ev);
(customEv->m_f)();
return true;
}
return false;
}
void setHandler(std::function<bool(const QEvent*)> handler)
{
m_handler = std::move(handler);
}
bool eventFilter(QObject* obj, QEvent* ev) override
{
if (m_handler && m_handler(ev))
{
return true;
}
return QObject::eventFilter(obj, ev);
}
private:
std::function<bool(const QEvent*)> m_handler;
};
struct QtEventLoopWrapper
{
std::reference_wrapper<QtEventLoop> loop;
void post(std::function<void()> fn)
{
loop.get().post(std::move(fn));
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment