psemek/libs/ui/source/controller.cpp

107 lines
2.2 KiB
C++

#include <psemek/ui/controller.hpp>
#include <psemek/ui/painter_impl.hpp>
#include <psemek/async/event_loop.hpp>
#include <psemek/gfx/gl.hpp>
#include <psemek/util/recursive.hpp>
#include <psemek/geom/camera.hpp>
namespace psemek::ui
{
struct controller::impl
{
painter_impl painter;
std::shared_ptr<element> root;
async::event_loop loop;
geom::box<float, 2> shape{{{0.f, 0.f}, {0.f, 0.f}}};
template <typename E>
bool event(E const & e);
};
template <typename E>
bool controller::impl::event(E const & e)
{
auto visitor = util::recursive([&](auto && self, element * elem) -> bool {
for (auto c : elem->children())
if (c && self(c)) return true;
if (elem->enabled() && elem->on_event(e)) return true;
return false;
});
if (root)
return visitor(root.get());
return false;
}
controller::controller()
: pimpl_{make_impl()}
{}
controller::~controller() = default;
std::shared_ptr<element> controller::set_root(std::shared_ptr<element> r)
{
auto old = std::move(impl().root);
impl().root = std::move(r);
if (old) old->set_loop(nullptr);
if (impl().root)
{
impl().root->set_loop(&impl().loop);
impl().root->reshape(impl().shape);
}
return old;
}
element * controller::root()
{
return impl().root.get();
}
void controller::reshape(geom::box<float, 2> const & shape)
{
impl().shape = shape;
if (impl().root)
impl().root->reshape(impl().shape);
}
bool controller::event(mouse_move const & e)
{
return impl().event(e);
}
bool controller::event(mouse_click const & e)
{
return impl().event(e);
}
bool controller::event(mouse_wheel const & e)
{
return impl().event(e);
}
void controller::render(gfx::render_target const & rt)
{
rt.bind();
gl::Enable(gl::DEPTH_TEST);
gl::DepthFunc(gl::LEQUAL);
gl::Enable(gl::BLEND);
gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
auto visitor = util::recursive([&](auto && self, element * elem) -> void {
elem->draw(impl().painter);
for (auto c : elem->children())
if (c) self(c);
});
if (impl().root)
visitor(impl().root.get());
impl().painter.render(geom::window_camera{rt.viewport[0].length(), rt.viewport[1].length()}.transform());
}
}