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})
|
psemek_add_library(psemek-ui ${PSEMEK_UI_HEADERS} ${PSEMEK_UI_SOURCES})
|
||||||
target_include_directories(psemek-ui PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
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)
|
psemek_glob_tests(psemek-ui tests)
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,20 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <psemek/ui/impl/component.hpp>
|
#include <psemek/ui/impl/component.hpp>
|
||||||
|
#include <psemek/ui/impl/event_state.hpp>
|
||||||
|
#include <psemek/async/executor.hpp>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <any>
|
#include <any>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
namespace psemek::ui::impl
|
||||||
{
|
{
|
||||||
|
|
||||||
struct component_factory
|
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() {}
|
virtual ~component_factory() {}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -35,18 +35,19 @@ namespace psemek::ui::impl
|
||||||
struct component_factory_base
|
struct component_factory_base
|
||||||
: component_factory
|
: 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());
|
std::type_index type(value.type());
|
||||||
if (auto it = type_factories_.find(type); it != type_factories_.end())
|
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);
|
throw component_not_supported_exception(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Type, typename ImplType, typename Factory>
|
template <typename Type, typename ImplType, typename Factory>
|
||||||
void register_type(Factory && 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;
|
std::unique_ptr<ImplType> impl;
|
||||||
if (root)
|
if (root)
|
||||||
|
|
@ -64,8 +65,17 @@ namespace psemek::ui::impl
|
||||||
(void)this;
|
(void)this;
|
||||||
|
|
||||||
if (!impl)
|
if (!impl)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_invocable_v<Factory, event_state const &>)
|
||||||
|
impl = factory(state);
|
||||||
|
else
|
||||||
impl = factory();
|
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);
|
auto const & typed_value = *std::any_cast<Type>(&value);
|
||||||
|
|
||||||
impl->update(typed_value);
|
impl->update(typed_value);
|
||||||
|
|
@ -75,8 +85,11 @@ namespace psemek::ui::impl
|
||||||
impl->release_child_token();
|
impl->release_child_token();
|
||||||
if (typed_value.child)
|
if (typed_value.child)
|
||||||
{
|
{
|
||||||
impl->set_child_token(typed_value.child.subscribe([this, impl = impl.get()](std::any const & child){
|
impl->set_child_token(typed_value.child.subscribe([this, impl = impl.get(), &state, &delayed_executor, request_reshape](std::any const & child){
|
||||||
impl->set_child(reconciliate(impl->release_child(), 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));
|
}, true));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -88,7 +101,8 @@ namespace psemek::ui::impl
|
||||||
{
|
{
|
||||||
if (typed_value.children)
|
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();
|
impl->release_child_tokens();
|
||||||
|
|
||||||
util::hash_map<key, std::unique_ptr<component>> child_by_key;
|
util::hash_map<key, std::unique_ptr<component>> child_by_key;
|
||||||
|
|
@ -128,15 +142,20 @@ namespace psemek::ui::impl
|
||||||
{
|
{
|
||||||
if (children_values[i].element)
|
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();
|
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));
|
impl->set_children(std::move(children));
|
||||||
|
request_reshape();
|
||||||
|
});
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl->set_child_tokens(std::move(child_tokens));
|
impl->set_child_tokens(std::move(child_tokens));
|
||||||
|
request_reshape();
|
||||||
|
});
|
||||||
}, true));
|
}, true));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -154,11 +173,16 @@ namespace psemek::ui::impl
|
||||||
template <typename Type, typename ImplType>
|
template <typename Type, typename ImplType>
|
||||||
void register_type()
|
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:
|
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(mouse_button_event const & event);
|
||||||
bool on_event(key_event const & event);
|
bool on_event(key_event const & event);
|
||||||
|
|
||||||
void animate(float dt);
|
void update(float dt);
|
||||||
void draw(renderer & renderer);
|
void draw(renderer & renderer);
|
||||||
|
|
||||||
private:
|
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/controller.hpp>
|
||||||
|
#include <psemek/ui/impl/event_state.hpp>
|
||||||
|
#include <psemek/async/event_loop.hpp>
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
namespace psemek::ui::impl
|
||||||
{
|
{
|
||||||
|
|
@ -6,13 +8,13 @@ namespace psemek::ui::impl
|
||||||
struct controller::impl
|
struct controller::impl
|
||||||
{
|
{
|
||||||
component_factory & factory;
|
component_factory & factory;
|
||||||
|
event_state state;
|
||||||
geom::vector<int, 2> screen_size = {0, 0};
|
|
||||||
|
|
||||||
util::signal<>::subscription_token root_token;
|
util::signal<>::subscription_token root_token;
|
||||||
util::signal<>::subscription_token root_reshape_token;
|
|
||||||
std::unique_ptr<ui::impl::component> root;
|
std::unique_ptr<ui::impl::component> root;
|
||||||
|
|
||||||
|
async::event_loop delayed_queue;
|
||||||
|
|
||||||
impl(component_factory & factory)
|
impl(component_factory & factory)
|
||||||
: factory(factory)
|
: factory(factory)
|
||||||
{}
|
{}
|
||||||
|
|
@ -21,44 +23,26 @@ namespace psemek::ui::impl
|
||||||
{
|
{
|
||||||
root_token = ui.subscribe([this](std::any const & value)
|
root_token = ui.subscribe([this](std::any const & value)
|
||||||
{
|
{
|
||||||
root = factory.reconciliate(std::move(root), value);
|
root = factory.reconciliate(std::move(root), value, state, delayed_queue, [this]{ delayed_queue.post([this]{ reshape(); }); });
|
||||||
subscribe_reshape();
|
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
on_event(resize_event{screen_size});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void subscribe_reshape()
|
void reshape()
|
||||||
{
|
{
|
||||||
root_reshape_token = nullptr;
|
if (!root)
|
||||||
|
return;
|
||||||
|
|
||||||
if (root)
|
root->reshape({{{0.f, state.size[0]}, {0.f, state.size[1]}}});
|
||||||
root_reshape_token = root->size_constraints().subscribe([this](psemek::ui::impl::size_constraints const &)
|
on_event_impl(mouse_move_event{state.mouse, {0, 0}}, root.get());
|
||||||
{
|
|
||||||
do_reshape();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void do_reshape()
|
|
||||||
{
|
|
||||||
if (root)
|
|
||||||
root->reshape({{{0.f, screen_size[0]}, {0.f, screen_size[1]}}});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Event>
|
template <typename Event>
|
||||||
bool on_event(Event const & event)
|
bool on_event(Event const & event)
|
||||||
{
|
{
|
||||||
|
apply(state, event);
|
||||||
return on_event_impl(event, root.get());
|
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>
|
template <typename Event>
|
||||||
bool on_event_impl(Event const & event, component * element)
|
bool on_event_impl(Event const & event, component * element)
|
||||||
{
|
{
|
||||||
|
|
@ -72,8 +56,9 @@ namespace psemek::ui::impl
|
||||||
return element->on_event(event);
|
return element->on_event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void animate(float dt)
|
void update(float dt)
|
||||||
{
|
{
|
||||||
|
delayed_queue.pump();
|
||||||
animate_impl(dt, root.get());
|
animate_impl(dt, root.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,9 +132,9 @@ namespace psemek::ui::impl
|
||||||
return impl().on_event(event);
|
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)
|
void controller::draw(renderer & renderer)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue