New UI library internals redesign: add global event_state, add delayed events queue, fix reshaping & subscribing to size_constraints
This commit is contained in:
parent
7151fef7a8
commit
367f82a01f
6 changed files with 107 additions and 82 deletions
|
|
@ -3,6 +3,6 @@ file(GLOB_RECURSE PSEMEK_UI_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "sour
|
|||
|
||||
psemek_add_library(psemek-ui ${PSEMEK_UI_HEADERS} ${PSEMEK_UI_SOURCES})
|
||||
target_include_directories(psemek-ui PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_link_libraries(psemek-ui PUBLIC psemek-util psemek-react psemek-geom psemek-gfx psemek-app)
|
||||
target_link_libraries(psemek-ui PUBLIC psemek-util psemek-react psemek-geom psemek-async psemek-app)
|
||||
|
||||
psemek_glob_tests(psemek-ui tests)
|
||||
|
|
|
|||
|
|
@ -1,16 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ui/impl/component.hpp>
|
||||
#include <psemek/ui/impl/event_state.hpp>
|
||||
#include <psemek/async/executor.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <any>
|
||||
#include <functional>
|
||||
|
||||
namespace psemek::ui::impl
|
||||
{
|
||||
|
||||
struct component_factory
|
||||
{
|
||||
virtual std::unique_ptr<component> reconciliate(std::unique_ptr<component> root, std::any const & value) = 0;
|
||||
virtual std::unique_ptr<component> reconciliate(std::unique_ptr<component> root, std::any const & value,
|
||||
event_state const & state, async::executor & delayed_executor, std::function<void()> request_reshape) = 0;
|
||||
|
||||
virtual ~component_factory() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -35,18 +35,19 @@ namespace psemek::ui::impl
|
|||
struct component_factory_base
|
||||
: component_factory
|
||||
{
|
||||
std::unique_ptr<component> reconciliate(std::unique_ptr<component> root, std::any const & value) override
|
||||
std::unique_ptr<component> reconciliate(std::unique_ptr<component> root, std::any const & value, event_state const & state, async::executor & delayed_executor, std::function<void()> request_reshape) override
|
||||
{
|
||||
std::type_index type(value.type());
|
||||
if (auto it = type_factories_.find(type); it != type_factories_.end())
|
||||
return it->second(std::move(root), value);
|
||||
return it->second(std::move(root), value, state, delayed_executor, request_reshape);
|
||||
throw component_not_supported_exception(type);
|
||||
}
|
||||
|
||||
template <typename Type, typename ImplType, typename Factory>
|
||||
void register_type(Factory && factory)
|
||||
{
|
||||
type_factories_[typeid(Type)] = [this, factory = std::move(factory)](std::unique_ptr<component> root, std::any const & value)
|
||||
type_factories_[typeid(Type)] = [this, factory = std::move(factory)](std::unique_ptr<component> root, std::any const & value,
|
||||
event_state const & state, async::executor & delayed_executor, std::function<void()> request_reshape)
|
||||
{
|
||||
std::unique_ptr<ImplType> impl;
|
||||
if (root)
|
||||
|
|
@ -64,8 +65,17 @@ namespace psemek::ui::impl
|
|||
(void)this;
|
||||
|
||||
if (!impl)
|
||||
{
|
||||
if constexpr (std::is_invocable_v<Factory, event_state const &>)
|
||||
impl = factory(state);
|
||||
else
|
||||
impl = factory();
|
||||
|
||||
impl->size_constraints().subscribe([request_reshape](auto const &){ request_reshape(); }, react::forever);
|
||||
|
||||
request_reshape();
|
||||
}
|
||||
|
||||
auto const & typed_value = *std::any_cast<Type>(&value);
|
||||
|
||||
impl->update(typed_value);
|
||||
|
|
@ -75,8 +85,11 @@ namespace psemek::ui::impl
|
|||
impl->release_child_token();
|
||||
if (typed_value.child)
|
||||
{
|
||||
impl->set_child_token(typed_value.child.subscribe([this, impl = impl.get()](std::any const & child){
|
||||
impl->set_child(reconciliate(impl->release_child(), child));
|
||||
impl->set_child_token(typed_value.child.subscribe([this, impl = impl.get(), &state, &delayed_executor, request_reshape](std::any const & child){
|
||||
delayed_executor.post([this, impl, &state, &delayed_executor, request_reshape, child]{
|
||||
impl->set_child(reconciliate(impl->release_child(), child, state, delayed_executor, request_reshape));
|
||||
request_reshape();
|
||||
});
|
||||
}, true));
|
||||
}
|
||||
else
|
||||
|
|
@ -88,7 +101,8 @@ namespace psemek::ui::impl
|
|||
{
|
||||
if (typed_value.children)
|
||||
{
|
||||
impl->set_children_token(typed_value.children.subscribe([this, impl = impl.get()](std::vector<typename Type::element> const & children_values){
|
||||
impl->set_children_token(typed_value.children.subscribe([this, impl = impl.get(), &state, &delayed_executor, request_reshape](std::vector<typename Type::element> const & children_values){
|
||||
delayed_executor.post([this, impl, &state, &delayed_executor, request_reshape, children_values]{
|
||||
impl->release_child_tokens();
|
||||
|
||||
util::hash_map<key, std::unique_ptr<component>> child_by_key;
|
||||
|
|
@ -128,15 +142,20 @@ namespace psemek::ui::impl
|
|||
{
|
||||
if (children_values[i].element)
|
||||
{
|
||||
child_tokens[i] = children_values[i].element.subscribe([this, impl, i](std::any const & value){
|
||||
child_tokens[i] = children_values[i].element.subscribe([this, impl, &state, &delayed_executor, request_reshape, i](std::any const & value){
|
||||
delayed_executor.post([this, impl, &state, &delayed_executor, request_reshape, i, value]{
|
||||
auto children = impl->release_children();
|
||||
children[i] = reconciliate(std::move(children[i]), value);
|
||||
children[i] = reconciliate(std::move(children[i]), value, state, delayed_executor, request_reshape);
|
||||
impl->set_children(std::move(children));
|
||||
request_reshape();
|
||||
});
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
|
||||
impl->set_child_tokens(std::move(child_tokens));
|
||||
request_reshape();
|
||||
});
|
||||
}, true));
|
||||
}
|
||||
else
|
||||
|
|
@ -154,11 +173,16 @@ namespace psemek::ui::impl
|
|||
template <typename Type, typename ImplType>
|
||||
void register_type()
|
||||
{
|
||||
register_type<Type, ImplType>([]{ return std::make_unique<ImplType>(); });
|
||||
register_type<Type, ImplType>([](event_state const & state){
|
||||
if constexpr (std::is_constructible_v<ImplType, event_state const &>)
|
||||
return std::make_unique<ImplType>(state);
|
||||
else
|
||||
return std::make_unique<ImplType>();
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
util::hash_map<std::type_index, util::function<std::unique_ptr<component>(std::unique_ptr<component>, std::any const &)>> type_factories_;
|
||||
util::hash_map<std::type_index, util::function<std::unique_ptr<component>(std::unique_ptr<component>, std::any const &, event_state const &, async::executor &, std::function<void()>)>> type_factories_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ namespace psemek::ui::impl
|
|||
bool on_event(mouse_button_event const & event);
|
||||
bool on_event(key_event const & event);
|
||||
|
||||
void animate(float dt);
|
||||
void update(float dt);
|
||||
void draw(renderer & renderer);
|
||||
|
||||
private:
|
||||
|
|
|
|||
12
libs/ui/include/psemek/ui/impl/event_state.hpp
Normal file
12
libs/ui/include/psemek/ui/impl/event_state.hpp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/app/event_state.hpp>
|
||||
|
||||
namespace psemek::ui::impl
|
||||
{
|
||||
|
||||
using event_state = app::event_state;
|
||||
|
||||
using app::apply;
|
||||
|
||||
}
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
#include <psemek/ui/impl/controller.hpp>
|
||||
#include <psemek/ui/impl/event_state.hpp>
|
||||
#include <psemek/async/event_loop.hpp>
|
||||
|
||||
namespace psemek::ui::impl
|
||||
{
|
||||
|
|
@ -6,13 +8,13 @@ namespace psemek::ui::impl
|
|||
struct controller::impl
|
||||
{
|
||||
component_factory & factory;
|
||||
|
||||
geom::vector<int, 2> screen_size = {0, 0};
|
||||
event_state state;
|
||||
|
||||
util::signal<>::subscription_token root_token;
|
||||
util::signal<>::subscription_token root_reshape_token;
|
||||
std::unique_ptr<ui::impl::component> root;
|
||||
|
||||
async::event_loop delayed_queue;
|
||||
|
||||
impl(component_factory & factory)
|
||||
: factory(factory)
|
||||
{}
|
||||
|
|
@ -21,44 +23,26 @@ namespace psemek::ui::impl
|
|||
{
|
||||
root_token = ui.subscribe([this](std::any const & value)
|
||||
{
|
||||
root = factory.reconciliate(std::move(root), value);
|
||||
subscribe_reshape();
|
||||
root = factory.reconciliate(std::move(root), value, state, delayed_queue, [this]{ delayed_queue.post([this]{ reshape(); }); });
|
||||
}, true);
|
||||
|
||||
on_event(resize_event{screen_size});
|
||||
}
|
||||
|
||||
void subscribe_reshape()
|
||||
void reshape()
|
||||
{
|
||||
root_reshape_token = nullptr;
|
||||
if (!root)
|
||||
return;
|
||||
|
||||
if (root)
|
||||
root_reshape_token = root->size_constraints().subscribe([this](psemek::ui::impl::size_constraints const &)
|
||||
{
|
||||
do_reshape();
|
||||
});
|
||||
}
|
||||
|
||||
void do_reshape()
|
||||
{
|
||||
if (root)
|
||||
root->reshape({{{0.f, screen_size[0]}, {0.f, screen_size[1]}}});
|
||||
root->reshape({{{0.f, state.size[0]}, {0.f, state.size[1]}}});
|
||||
on_event_impl(mouse_move_event{state.mouse, {0, 0}}, root.get());
|
||||
}
|
||||
|
||||
template <typename Event>
|
||||
bool on_event(Event const & event)
|
||||
{
|
||||
apply(state, event);
|
||||
return on_event_impl(event, root.get());
|
||||
}
|
||||
|
||||
bool on_event(resize_event const & event)
|
||||
{
|
||||
screen_size = event.size;
|
||||
bool result = on_event_impl(event, root.get());
|
||||
do_reshape();
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Event>
|
||||
bool on_event_impl(Event const & event, component * element)
|
||||
{
|
||||
|
|
@ -72,8 +56,9 @@ namespace psemek::ui::impl
|
|||
return element->on_event(event);
|
||||
}
|
||||
|
||||
void animate(float dt)
|
||||
void update(float dt)
|
||||
{
|
||||
delayed_queue.pump();
|
||||
animate_impl(dt, root.get());
|
||||
}
|
||||
|
||||
|
|
@ -147,9 +132,9 @@ namespace psemek::ui::impl
|
|||
return impl().on_event(event);
|
||||
}
|
||||
|
||||
void controller::animate(float dt)
|
||||
void controller::update(float dt)
|
||||
{
|
||||
impl().animate(dt);
|
||||
impl().update(dt);
|
||||
}
|
||||
|
||||
void controller::draw(renderer & renderer)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue