diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt index 819171b9..873d301f 100644 --- a/libs/ui/CMakeLists.txt +++ b/libs/ui/CMakeLists.txt @@ -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) diff --git a/libs/ui/include/psemek/ui/impl/component_factory.hpp b/libs/ui/include/psemek/ui/impl/component_factory.hpp index 8b95602d..aee2b8dd 100644 --- a/libs/ui/include/psemek/ui/impl/component_factory.hpp +++ b/libs/ui/include/psemek/ui/impl/component_factory.hpp @@ -1,16 +1,20 @@ #pragma once #include +#include +#include #include #include +#include namespace psemek::ui::impl { struct component_factory { - virtual std::unique_ptr reconciliate(std::unique_ptr root, std::any const & value) = 0; + virtual std::unique_ptr reconciliate(std::unique_ptr root, std::any const & value, + event_state const & state, async::executor & delayed_executor, std::function request_reshape) = 0; virtual ~component_factory() {} }; diff --git a/libs/ui/include/psemek/ui/impl/component_factory_base.hpp b/libs/ui/include/psemek/ui/impl/component_factory_base.hpp index 0ea05390..a60c60bb 100644 --- a/libs/ui/include/psemek/ui/impl/component_factory_base.hpp +++ b/libs/ui/include/psemek/ui/impl/component_factory_base.hpp @@ -35,18 +35,19 @@ namespace psemek::ui::impl struct component_factory_base : component_factory { - std::unique_ptr reconciliate(std::unique_ptr root, std::any const & value) override + std::unique_ptr reconciliate(std::unique_ptr root, std::any const & value, event_state const & state, async::executor & delayed_executor, std::function 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 void register_type(Factory && factory) { - type_factories_[typeid(Type)] = [this, factory = std::move(factory)](std::unique_ptr root, std::any const & value) + type_factories_[typeid(Type)] = [this, factory = std::move(factory)](std::unique_ptr root, std::any const & value, + event_state const & state, async::executor & delayed_executor, std::function request_reshape) { std::unique_ptr impl; if (root) @@ -64,7 +65,16 @@ namespace psemek::ui::impl (void)this; if (!impl) - impl = factory(); + { + if constexpr (std::is_invocable_v) + 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(&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,55 +101,61 @@ namespace psemek::ui::impl { if (typed_value.children) { - impl->set_children_token(typed_value.children.subscribe([this, impl = impl.get()](std::vector const & children_values){ - impl->release_child_tokens(); + impl->set_children_token(typed_value.children.subscribe([this, impl = impl.get(), &state, &delayed_executor, request_reshape](std::vector const & children_values){ + delayed_executor.post([this, impl, &state, &delayed_executor, request_reshape, children_values]{ + impl->release_child_tokens(); - util::hash_map> child_by_key; + util::hash_map> child_by_key; - auto old_children = impl->release_children(); - { - auto keys = impl->release_child_keys(); - - for (std::size_t i = 0; i < old_children.size(); ++i) - if (keys[i]) - child_by_key[std::move(*keys[i])] = std::move(old_children[i]); - } - - std::vector> children(children_values.size()); - std::vector> child_keys(children_values.size()); - - for (std::size_t i = 0; i < children_values.size(); ++i) - { - if (auto const & key = children_values[i].key) + auto old_children = impl->release_children(); { - if (auto it = child_by_key.find(*key); it != child_by_key.end()) - children[i] = std::move(it->second); + auto keys = impl->release_child_keys(); - child_keys[i] = *key; + for (std::size_t i = 0; i < old_children.size(); ++i) + if (keys[i]) + child_by_key[std::move(*keys[i])] = std::move(old_children[i]); } - if (!children[i] && i < old_children.size() && old_children[i]) - children[i] = std::move(old_children[i]); - } + std::vector> children(children_values.size()); + std::vector> child_keys(children_values.size()); - impl->set_children(std::move(children)); - impl->set_child_keys(std::move(child_keys)); - - std::vector::subscription_token> child_tokens(children_values.size()); - - for (std::size_t i = 0; i < children_values.size(); ++i) - { - if (children_values[i].element) + for (std::size_t i = 0; i < children_values.size(); ++i) { - child_tokens[i] = children_values[i].element.subscribe([this, impl, i](std::any const & value){ - auto children = impl->release_children(); - children[i] = reconciliate(std::move(children[i]), value); - impl->set_children(std::move(children)); - }, true); - } - } + if (auto const & key = children_values[i].key) + { + if (auto it = child_by_key.find(*key); it != child_by_key.end()) + children[i] = std::move(it->second); - impl->set_child_tokens(std::move(child_tokens)); + child_keys[i] = *key; + } + + if (!children[i] && i < old_children.size() && old_children[i]) + children[i] = std::move(old_children[i]); + } + + impl->set_children(std::move(children)); + impl->set_child_keys(std::move(child_keys)); + + std::vector::subscription_token> child_tokens(children_values.size()); + + for (std::size_t i = 0; i < children_values.size(); ++i) + { + if (children_values[i].element) + { + 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, 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 void register_type() { - register_type([]{ return std::make_unique(); }); + register_type([](event_state const & state){ + if constexpr (std::is_constructible_v) + return std::make_unique(state); + else + return std::make_unique(); + }); } private: - util::hash_map(std::unique_ptr, std::any const &)>> type_factories_; + util::hash_map(std::unique_ptr, std::any const &, event_state const &, async::executor &, std::function)>> type_factories_; }; } diff --git a/libs/ui/include/psemek/ui/impl/controller.hpp b/libs/ui/include/psemek/ui/impl/controller.hpp index 864325a6..cdb5807a 100644 --- a/libs/ui/include/psemek/ui/impl/controller.hpp +++ b/libs/ui/include/psemek/ui/impl/controller.hpp @@ -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: diff --git a/libs/ui/include/psemek/ui/impl/event_state.hpp b/libs/ui/include/psemek/ui/impl/event_state.hpp new file mode 100644 index 00000000..0d89885a --- /dev/null +++ b/libs/ui/include/psemek/ui/impl/event_state.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace psemek::ui::impl +{ + + using event_state = app::event_state; + + using app::apply; + +} diff --git a/libs/ui/source/impl/controller.cpp b/libs/ui/source/impl/controller.cpp index 128bdc34..17441ec4 100644 --- a/libs/ui/source/impl/controller.cpp +++ b/libs/ui/source/impl/controller.cpp @@ -1,4 +1,6 @@ #include +#include +#include namespace psemek::ui::impl { @@ -6,13 +8,13 @@ namespace psemek::ui::impl struct controller::impl { component_factory & factory; - - geom::vector screen_size = {0, 0}; + event_state state; util::signal<>::subscription_token root_token; - util::signal<>::subscription_token root_reshape_token; std::unique_ptr 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 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 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)