Farewell to reactive ui library
This commit is contained in:
parent
4fde43313c
commit
dbd479d413
64 changed files with 0 additions and 3263 deletions
|
|
@ -1,8 +0,0 @@
|
||||||
file(GLOB_RECURSE PSEMEK_UI_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "include/*.hpp")
|
|
||||||
file(GLOB_RECURSE PSEMEK_UI_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "source/*.cpp")
|
|
||||||
|
|
||||||
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-fonts psemek-async psemek-app)
|
|
||||||
|
|
||||||
psemek_glob_tests(psemek-ui tests)
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/alignment.hpp>
|
|
||||||
#include <psemek/react/value.hpp>
|
|
||||||
|
|
||||||
#include <any>
|
|
||||||
|
|
||||||
namespace psemek::ui
|
|
||||||
{
|
|
||||||
|
|
||||||
struct aligned
|
|
||||||
{
|
|
||||||
react::value<std::any> child = {};
|
|
||||||
react::value<halignment> halign = halignment::center;
|
|
||||||
react::value<valignment> valign = valignment::center;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/geom/interval.hpp>
|
|
||||||
#include <psemek/util/enum.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui
|
|
||||||
{
|
|
||||||
|
|
||||||
enum class halignment
|
|
||||||
{
|
|
||||||
left,
|
|
||||||
center,
|
|
||||||
right,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class valignment
|
|
||||||
{
|
|
||||||
top,
|
|
||||||
center,
|
|
||||||
bottom,
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept any_alignment = std::is_same_v<T, halignment> || std::is_same_v<T, valignment>;
|
|
||||||
|
|
||||||
inline float lerp_factor(halignment h)
|
|
||||||
{
|
|
||||||
switch (h)
|
|
||||||
{
|
|
||||||
case halignment::left: return 0;
|
|
||||||
case halignment::center: return 0.5f;
|
|
||||||
case halignment::right: return 1.f;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw util::unknown_enum_value_exception{h};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline float lerp_factor(valignment h)
|
|
||||||
{
|
|
||||||
switch (h)
|
|
||||||
{
|
|
||||||
case valignment::top: return 0.f;
|
|
||||||
case valignment::center: return 0.5f;
|
|
||||||
case valignment::bottom: return 1.f;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw util::unknown_enum_value_exception{h};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <any_alignment Alignment>
|
|
||||||
inline float align(geom::interval<float> const & range, Alignment a)
|
|
||||||
{
|
|
||||||
return geom::lerp(range, lerp_factor(a));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <any_alignment Alignment>
|
|
||||||
inline geom::interval<float> align(geom::interval<float> const & range, float size, Alignment a)
|
|
||||||
{
|
|
||||||
return geom::interval{range.min, range.min + size} + (range.length() - size) * lerp_factor(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/key.hpp>
|
|
||||||
#include <psemek/react/value.hpp>
|
|
||||||
|
|
||||||
#include <any>
|
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
namespace psemek::ui
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace box_layout
|
|
||||||
{
|
|
||||||
|
|
||||||
struct minimized
|
|
||||||
{};
|
|
||||||
|
|
||||||
struct weight
|
|
||||||
{
|
|
||||||
float value = 1.f;
|
|
||||||
};
|
|
||||||
|
|
||||||
using size_policy = std::variant<minimized, weight>;
|
|
||||||
|
|
||||||
struct element
|
|
||||||
{
|
|
||||||
react::value<std::any> element;
|
|
||||||
react::value<size_policy> policy = {};
|
|
||||||
std::optional<ui::key> key = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct horizontal
|
|
||||||
{
|
|
||||||
using element = box_layout::element;
|
|
||||||
react::value<std::vector<element>> children = {};
|
|
||||||
react::value<float> padding = 0.f;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct vertical
|
|
||||||
{
|
|
||||||
using element = box_layout::element;
|
|
||||||
react::value<std::vector<element>> children = {};
|
|
||||||
react::value<float> padding = 0.f;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/react/value.hpp>
|
|
||||||
#include <psemek/react/source.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui
|
|
||||||
{
|
|
||||||
|
|
||||||
struct button
|
|
||||||
{
|
|
||||||
react::source<bool> mouseover = {};
|
|
||||||
react::source<bool> mousedown = {};
|
|
||||||
react::source<bool> on_mouseover = {};
|
|
||||||
react::source<bool> on_mousedown = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/react/value.hpp>
|
|
||||||
|
|
||||||
#include <any>
|
|
||||||
|
|
||||||
namespace psemek::ui
|
|
||||||
{
|
|
||||||
|
|
||||||
struct extend
|
|
||||||
{
|
|
||||||
react::value<std::any> child = {};
|
|
||||||
react::value<float> left = 0.f;
|
|
||||||
react::value<float> right = 0.f;
|
|
||||||
react::value<float> top = 0.f;
|
|
||||||
react::value<float> bottom = 0.f;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/react/value.hpp>
|
|
||||||
#include <psemek/geom/vector.hpp>
|
|
||||||
|
|
||||||
#include <any>
|
|
||||||
|
|
||||||
namespace psemek::ui
|
|
||||||
{
|
|
||||||
|
|
||||||
struct fixed_size
|
|
||||||
{
|
|
||||||
react::value<std::any> child = {};
|
|
||||||
react::value<geom::vector<float, 2>> size = {0.f, 0.f};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct fixed_width
|
|
||||||
{
|
|
||||||
react::value<std::any> child = {};
|
|
||||||
react::value<float> width = 0.f;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct fixed_height
|
|
||||||
{
|
|
||||||
react::value<std::any> child = {};
|
|
||||||
react::value<float> height = 0.f;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/aligned.hpp>
|
|
||||||
#include <psemek/react/value.hpp>
|
|
||||||
#include <psemek/geom/point.hpp>
|
|
||||||
|
|
||||||
#include <any>
|
|
||||||
|
|
||||||
namespace psemek::ui
|
|
||||||
{
|
|
||||||
|
|
||||||
struct floating
|
|
||||||
{
|
|
||||||
react::value<std::any> child = {};
|
|
||||||
react::value<geom::point<float, 2>> anchor = {0.f, 0.f};
|
|
||||||
react::value<halignment> halign = halignment::center;
|
|
||||||
react::value<valignment> valign = valignment::center;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/react/value.hpp>
|
|
||||||
|
|
||||||
#include <any>
|
|
||||||
|
|
||||||
namespace psemek::ui
|
|
||||||
{
|
|
||||||
|
|
||||||
struct frame
|
|
||||||
{
|
|
||||||
react::value<std::any> child = {};
|
|
||||||
react::value<float> margin = 0.f;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/aligned.hpp>
|
|
||||||
#include <psemek/ui/impl/single_container.hpp>
|
|
||||||
#include <psemek/react/source.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
struct aligned_base
|
|
||||||
: single_container
|
|
||||||
{
|
|
||||||
aligned_base();
|
|
||||||
|
|
||||||
void reshape(geom::box<float, 2> const & new_shape) override;
|
|
||||||
react::value<struct size_constraints> size_constraints() const override;
|
|
||||||
|
|
||||||
void set_child(std::unique_ptr<component> child) override;
|
|
||||||
std::unique_ptr<component> release_child() override;
|
|
||||||
|
|
||||||
void update(aligned const & value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
react::source<react::value<std::pair<halignment, valignment>>> align_;
|
|
||||||
react::source<react::value<struct size_constraints>> child_size_constraints_;
|
|
||||||
react::value<struct size_constraints> size_constraints_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/impl/container.hpp>
|
|
||||||
#include <psemek/ui/box_layout.hpp>
|
|
||||||
#include <psemek/react/value.hpp>
|
|
||||||
#include <psemek/react/source.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
|
|
||||||
template <int Dimension>
|
|
||||||
struct box_layout_type;
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct box_layout_type<0>
|
|
||||||
{
|
|
||||||
using type = box_layout::horizontal;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct box_layout_type<1>
|
|
||||||
{
|
|
||||||
using type = box_layout::vertical;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dimension == 0 is horizontal layout
|
|
||||||
// Dimension == 1 is vertical layout
|
|
||||||
template <int Dimension>
|
|
||||||
struct box_layout_base
|
|
||||||
: container
|
|
||||||
{
|
|
||||||
box_layout_base();
|
|
||||||
|
|
||||||
void reshape(geom::box<float, 2> const & new_shape) override;
|
|
||||||
react::value<struct size_constraints> size_constraints() const override;
|
|
||||||
|
|
||||||
void set_children(std::vector<std::unique_ptr<component>> children) override;
|
|
||||||
std::vector<std::unique_ptr<component>> release_children() override;
|
|
||||||
|
|
||||||
void update(typename detail::box_layout_type<Dimension>::type const & box_layout);
|
|
||||||
|
|
||||||
private:
|
|
||||||
react::source<react::value<float>> padding_;
|
|
||||||
react::source<react::value<std::vector<react::value<box_layout::size_policy>>>> size_policies_;
|
|
||||||
react::source<std::vector<react::value<struct size_constraints>>> children_size_constraints_;
|
|
||||||
react::value<struct size_constraints> size_constraints_;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern template struct box_layout_base<0>;
|
|
||||||
extern template struct box_layout_base<1>;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/button.hpp>
|
|
||||||
#include <psemek/ui/impl/component.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
struct button_base
|
|
||||||
: component
|
|
||||||
{
|
|
||||||
button_base();
|
|
||||||
|
|
||||||
bool on_event(mouse_move_event const & event) override;
|
|
||||||
bool on_event(mouse_button_event const & event) override;
|
|
||||||
|
|
||||||
void update(button const & value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
react::source<bool> mouseover_;
|
|
||||||
react::source<bool> mousedown_;
|
|
||||||
react::source<bool> on_mouseover_;
|
|
||||||
react::source<bool> on_mousedown_;
|
|
||||||
|
|
||||||
bool is_mouseover_ = false;
|
|
||||||
bool is_mousedown_ = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/impl/size_constraints.hpp>
|
|
||||||
#include <psemek/ui/impl/events.hpp>
|
|
||||||
#include <psemek/ui/impl/renderer.hpp>
|
|
||||||
#include <psemek/react/value.hpp>
|
|
||||||
#include <psemek/geom/box.hpp>
|
|
||||||
#include <psemek/geom/interval.hpp>
|
|
||||||
#include <psemek/util/span.hpp>
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
struct component
|
|
||||||
{
|
|
||||||
virtual util::span<std::unique_ptr<component> const> children() const;
|
|
||||||
|
|
||||||
virtual geom::box<float, 2> const & shape() const;
|
|
||||||
virtual void reshape(geom::box<float, 2> const & new_shape);
|
|
||||||
|
|
||||||
virtual react::value<struct size_constraints> size_constraints() const;
|
|
||||||
|
|
||||||
virtual bool on_event(resize_event const &) { return false; }
|
|
||||||
virtual bool on_event(mouse_move_event const &) { return false; }
|
|
||||||
virtual bool on_event(mouse_wheel_event const &) { return false; }
|
|
||||||
virtual bool on_event(mouse_button_event const &) { return false; }
|
|
||||||
virtual bool on_event(key_event const &) { return false; }
|
|
||||||
|
|
||||||
virtual void animate(float) {}
|
|
||||||
|
|
||||||
virtual void draw(renderer &) {}
|
|
||||||
virtual void post_draw(renderer &) {}
|
|
||||||
|
|
||||||
virtual std::optional<std::string> cursor() const { return std::nullopt; }
|
|
||||||
|
|
||||||
virtual ~component() {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
geom::box<float, 2> shape_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
#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,
|
|
||||||
event_state const & state, async::executor & delayed_executor, std::function<void()> request_reshape) = 0;
|
|
||||||
|
|
||||||
virtual ~component_factory() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,194 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/impl/component_factory.hpp>
|
|
||||||
#include <psemek/ui/impl/single_container.hpp>
|
|
||||||
#include <psemek/ui/impl/container.hpp>
|
|
||||||
#include <psemek/react/value.hpp>
|
|
||||||
#include <psemek/util/to_string.hpp>
|
|
||||||
#include <psemek/util/type_name.hpp>
|
|
||||||
#include <psemek/util/function.hpp>
|
|
||||||
#include <psemek/util/hash_table.hpp>
|
|
||||||
#include <psemek/util/exception.hpp>
|
|
||||||
|
|
||||||
#include <typeindex>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
struct component_not_supported_exception
|
|
||||||
: util::exception
|
|
||||||
{
|
|
||||||
component_not_supported_exception(std::type_index type, boost::stacktrace::stacktrace stacktrace = {})
|
|
||||||
: util::exception(util::to_string("UI component ", util::type_name(type), " is not supported"), std::move(stacktrace))
|
|
||||||
, type_(type)
|
|
||||||
{}
|
|
||||||
|
|
||||||
std::type_index type() const
|
|
||||||
{
|
|
||||||
return type_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::type_index type_;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct component_factory_base
|
|
||||||
: component_factory
|
|
||||||
{
|
|
||||||
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, 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,
|
|
||||||
event_state const & state, async::executor & delayed_executor, std::function<void()> request_reshape)
|
|
||||||
{
|
|
||||||
std::unique_ptr<ImplType> impl;
|
|
||||||
if (root)
|
|
||||||
{
|
|
||||||
if (auto impl_raw = dynamic_cast<ImplType *>(root.get()))
|
|
||||||
{
|
|
||||||
impl.reset(impl_raw);
|
|
||||||
root.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
root.reset();
|
|
||||||
|
|
||||||
// prevent 'lambda capture is unused' warning
|
|
||||||
(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);
|
|
||||||
|
|
||||||
if constexpr (std::is_base_of_v<single_container, ImplType>)
|
|
||||||
{
|
|
||||||
impl->release_child_token();
|
|
||||||
if (typed_value.child)
|
|
||||||
{
|
|
||||||
impl->set_child_token(typed_value.child.subscribe([this, impl = impl.get(), &state, &delayed_executor, request_reshape, holder = typed_value.child](std::any const & child){
|
|
||||||
delayed_executor.post([this, impl, &state, &delayed_executor, request_reshape, child]{
|
|
||||||
if (child.has_value())
|
|
||||||
impl->set_child(reconciliate(impl->release_child(), child, state, delayed_executor, request_reshape));
|
|
||||||
else
|
|
||||||
impl->release_child();
|
|
||||||
request_reshape();
|
|
||||||
});
|
|
||||||
}, true));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
impl->release_child();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if constexpr (std::is_base_of_v<container, ImplType>)
|
|
||||||
{
|
|
||||||
if (typed_value.children)
|
|
||||||
{
|
|
||||||
impl->set_children_token(typed_value.children.subscribe([this, impl = impl.get(), &state, &delayed_executor, request_reshape, holder = typed_value.children](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;
|
|
||||||
|
|
||||||
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<std::unique_ptr<component>> children(children_values.size());
|
|
||||||
std::vector<std::optional<key>> child_keys(children_values.size());
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < children_values.size(); ++i)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
child_keys[i] = *key;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!children[i] && i < old_children.size() && old_children[i] && children_values[i].element)
|
|
||||||
children[i] = std::move(old_children[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl->set_children(std::move(children));
|
|
||||||
impl->set_child_keys(std::move(child_keys));
|
|
||||||
|
|
||||||
std::vector<util::signal<std::any>::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, holder = children_values[i].element](std::any const & value){
|
|
||||||
delayed_executor.post([this, impl, &state, &delayed_executor, request_reshape, i, value]{
|
|
||||||
auto children = impl->release_children();
|
|
||||||
if (value.has_value())
|
|
||||||
children[i] = reconciliate(std::move(children[i]), value, state, delayed_executor, request_reshape);
|
|
||||||
else
|
|
||||||
children[i] = nullptr;
|
|
||||||
impl->set_children(std::move(children));
|
|
||||||
request_reshape();
|
|
||||||
});
|
|
||||||
}, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl->set_child_tokens(std::move(child_tokens));
|
|
||||||
request_reshape();
|
|
||||||
});
|
|
||||||
}, true));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
impl->release_child_tokens();
|
|
||||||
impl->release_child_keys();
|
|
||||||
impl->release_children();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return impl;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Type, typename ImplType>
|
|
||||||
void register_type()
|
|
||||||
{
|
|
||||||
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 &, event_state const &, async::executor &, std::function<void()>)>> type_factories_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/impl/component.hpp>
|
|
||||||
#include <psemek/ui/key.hpp>
|
|
||||||
#include <psemek/util/signal.hpp>
|
|
||||||
|
|
||||||
#include <any>
|
|
||||||
#include <optional>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
struct container
|
|
||||||
: component
|
|
||||||
{
|
|
||||||
virtual void set_children(std::vector<std::unique_ptr<component>> children);
|
|
||||||
virtual void set_children_token(util::signal<std::vector<std::any>>::subscription_token token);
|
|
||||||
virtual void set_child_tokens(std::vector<util::signal<std::any>::subscription_token> tokens);
|
|
||||||
virtual void set_child_keys(std::vector<std::optional<key>> keys);
|
|
||||||
|
|
||||||
virtual std::vector<std::unique_ptr<component>> release_children();
|
|
||||||
virtual util::signal<>::subscription_token release_children_token();
|
|
||||||
virtual std::vector<util::signal<>::subscription_token> release_child_tokens();
|
|
||||||
virtual std::vector<std::optional<key>> release_child_keys();
|
|
||||||
|
|
||||||
util::span<std::unique_ptr<component> const> children() const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
util::signal<>::subscription_token children_token_;
|
|
||||||
std::vector<std::unique_ptr<component>> children_;
|
|
||||||
std::vector<util::signal<>::subscription_token> child_tokens_;
|
|
||||||
std::vector<std::optional<key>> child_keys_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/impl/component_factory.hpp>
|
|
||||||
#include <psemek/ui/impl/renderer.hpp>
|
|
||||||
#include <psemek/ui/impl/event_state.hpp>
|
|
||||||
#include <psemek/react/value.hpp>
|
|
||||||
#include <psemek/util/pimpl.hpp>
|
|
||||||
|
|
||||||
#include <any>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
struct controller
|
|
||||||
{
|
|
||||||
controller(component_factory & factory);
|
|
||||||
~controller();
|
|
||||||
|
|
||||||
void set_ui(react::value<std::any> ui);
|
|
||||||
|
|
||||||
component * root();
|
|
||||||
|
|
||||||
bool on_event(resize_event const & event);
|
|
||||||
bool on_event(mouse_move_event const & event);
|
|
||||||
bool on_event(mouse_wheel_event const & event);
|
|
||||||
bool on_event(mouse_button_event const & event);
|
|
||||||
bool on_event(key_event const & event);
|
|
||||||
|
|
||||||
event_state const & state();
|
|
||||||
|
|
||||||
void update(float dt);
|
|
||||||
void draw(renderer & renderer);
|
|
||||||
|
|
||||||
std::optional<std::string> cursor();
|
|
||||||
|
|
||||||
private:
|
|
||||||
psemek_declare_pimpl
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/impl/component_factory_base.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
struct default_component_factory
|
|
||||||
: component_factory_base
|
|
||||||
{
|
|
||||||
default_component_factory();
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/app/event_state.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
using event_state = app::event_state;
|
|
||||||
|
|
||||||
using app::apply;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/app/events.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
using app::resize_event;
|
|
||||||
using app::mouse_move_event;
|
|
||||||
using app::mouse_button_event;
|
|
||||||
using app::mouse_wheel_event;
|
|
||||||
using app::key_event;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/extend.hpp>
|
|
||||||
#include <psemek/ui/impl/single_container.hpp>
|
|
||||||
#include <psemek/react/source.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
struct extend_base
|
|
||||||
: single_container
|
|
||||||
{
|
|
||||||
extend_base();
|
|
||||||
|
|
||||||
void reshape(geom::box<float, 2> const & new_shape) override;
|
|
||||||
react::value<struct size_constraints> size_constraints() const override;
|
|
||||||
|
|
||||||
void set_child(std::unique_ptr<component> child) override;
|
|
||||||
std::unique_ptr<component> release_child() override;
|
|
||||||
|
|
||||||
void update(extend const & value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
react::source<react::value<float>> left_;
|
|
||||||
react::source<react::value<float>> right_;
|
|
||||||
react::source<react::value<float>> top_;
|
|
||||||
react::source<react::value<float>> bottom_;
|
|
||||||
react::source<react::value<struct size_constraints>> child_size_constraints_;
|
|
||||||
react::value<struct size_constraints> size_constraints_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/fixed_size.hpp>
|
|
||||||
#include <psemek/ui/impl/single_container.hpp>
|
|
||||||
#include <psemek/react/source.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
struct fixed_size_base
|
|
||||||
: single_container
|
|
||||||
{
|
|
||||||
fixed_size_base();
|
|
||||||
|
|
||||||
void reshape(geom::box<float, 2> const & new_shape) override;
|
|
||||||
react::value<struct size_constraints> size_constraints() const override;
|
|
||||||
|
|
||||||
void update(fixed_size const & value);
|
|
||||||
void update(fixed_width const & value);
|
|
||||||
void update(fixed_height const & value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
react::source<react::value<geom::vector<std::optional<float>, 2>>> size_;
|
|
||||||
react::source<react::value<struct size_constraints>> child_size_constraints_;
|
|
||||||
react::value<struct size_constraints> size_constraints_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/floating.hpp>
|
|
||||||
#include <psemek/ui/impl/single_container.hpp>
|
|
||||||
#include <psemek/react/source.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
struct floating_base
|
|
||||||
: single_container
|
|
||||||
{
|
|
||||||
floating_base();
|
|
||||||
|
|
||||||
void reshape(geom::box<float, 2> const & new_shape) override;
|
|
||||||
react::value<struct size_constraints> size_constraints() const override;
|
|
||||||
|
|
||||||
void update(floating const & value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
react::value<geom::point<float, 2>> anchor_ = geom::point{0.f, 0.f};
|
|
||||||
react::value<halignment> halign_ = halignment::center;
|
|
||||||
react::value<valignment> valign_ = valignment::center;
|
|
||||||
|
|
||||||
react::value<struct size_constraints> size_constraints_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/frame.hpp>
|
|
||||||
#include <psemek/ui/impl/single_container.hpp>
|
|
||||||
#include <psemek/react/source.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
struct frame_base
|
|
||||||
: single_container
|
|
||||||
{
|
|
||||||
frame_base();
|
|
||||||
|
|
||||||
void reshape(geom::box<float, 2> const & new_shape) override;
|
|
||||||
react::value<struct size_constraints> size_constraints() const override;
|
|
||||||
|
|
||||||
void set_child(std::unique_ptr<component> child) override;
|
|
||||||
std::unique_ptr<component> release_child() override;
|
|
||||||
|
|
||||||
void update(frame const & value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
react::source<react::value<float>> margin_;
|
|
||||||
react::source<react::value<struct size_constraints>> child_size_constraints_;
|
|
||||||
react::value<struct size_constraints> size_constraints_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/label.hpp>
|
|
||||||
#include <psemek/ui/impl/component.hpp>
|
|
||||||
#include <psemek/react/source.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
struct label_base
|
|
||||||
: component
|
|
||||||
{
|
|
||||||
label_base();
|
|
||||||
|
|
||||||
react::value<struct size_constraints> size_constraints() const override;
|
|
||||||
|
|
||||||
void draw(renderer & renderer) override;
|
|
||||||
|
|
||||||
void update(label const & value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
react::source<react::value<fonts::font *>> font_;
|
|
||||||
react::source<react::value<std::vector<fonts::shaped_glyph>>> glyphs_;
|
|
||||||
react::value<gfx::color_rgba> color_ = gfx::color_rgba{0, 0, 0, 255};
|
|
||||||
react::value<halignment> halign_ = halignment::center;
|
|
||||||
react::value<valignment> valign_ = valignment::center;
|
|
||||||
react::value<geom::vector<float, 2>> size_;
|
|
||||||
react::value<struct size_constraints> size_constraints_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/move.hpp>
|
|
||||||
#include <psemek/ui/impl/single_container.hpp>
|
|
||||||
#include <psemek/react/source.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
struct move_base
|
|
||||||
: single_container
|
|
||||||
{
|
|
||||||
move_base();
|
|
||||||
|
|
||||||
void reshape(geom::box<float, 2> const & new_shape) override;
|
|
||||||
react::value<struct size_constraints> size_constraints() const override;
|
|
||||||
|
|
||||||
void set_child(std::unique_ptr<component> child) override;
|
|
||||||
std::unique_ptr<component> release_child() override;
|
|
||||||
|
|
||||||
void animate(float dt) override;
|
|
||||||
void update(move const & value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
react::value<geom::vector<float, 2>> offset_;
|
|
||||||
react::value<float> animation_speed_;
|
|
||||||
react::source<react::value<struct size_constraints>> child_size_constraints_;
|
|
||||||
react::value<struct size_constraints> size_constraints_;
|
|
||||||
|
|
||||||
geom::vector<float, 2> current_offset_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/fonts/font_v2.hpp>
|
|
||||||
#include <psemek/gfx/color.hpp>
|
|
||||||
#include <psemek/geom/box.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
struct renderer
|
|
||||||
{
|
|
||||||
virtual void draw_glyph(fonts::texture_type const & texture, geom::box<float, 2> const & position, geom::box<float, 2> const & texcoord, gfx::color_rgba const & color) = 0;
|
|
||||||
|
|
||||||
virtual ~renderer() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/shape_reader.hpp>
|
|
||||||
#include <psemek/ui/impl/single_container.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
struct shape_reader_base
|
|
||||||
: single_container
|
|
||||||
{
|
|
||||||
shape_reader_base();
|
|
||||||
|
|
||||||
void reshape(geom::box<float, 2> const & new_shape) override;
|
|
||||||
react::value<struct size_constraints> size_constraints() const override;
|
|
||||||
|
|
||||||
void update(shape_reader const & value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
react::source<geom::box<float, 2>> shape_ = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/impl/component.hpp>
|
|
||||||
#include <psemek/ui/key.hpp>
|
|
||||||
#include <psemek/util/signal.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
struct single_container
|
|
||||||
: component
|
|
||||||
{
|
|
||||||
util::span<std::unique_ptr<component> const> children() const override;
|
|
||||||
|
|
||||||
virtual void set_child(std::unique_ptr<component> child);
|
|
||||||
virtual void set_child_token(util::signal<>::subscription_token token);
|
|
||||||
virtual component * child() const;
|
|
||||||
virtual std::unique_ptr<component> release_child();
|
|
||||||
virtual util::signal<>::subscription_token release_child_token();
|
|
||||||
|
|
||||||
private:
|
|
||||||
util::signal<>::subscription_token child_token_;
|
|
||||||
std::unique_ptr<component> child_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/impl/single_container.hpp>
|
|
||||||
#include <psemek/react/value.hpp>
|
|
||||||
#include <psemek/react/source.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
struct single_container_base
|
|
||||||
: single_container
|
|
||||||
{
|
|
||||||
single_container_base(react::value<geom::vector<float, 2>> margin);
|
|
||||||
|
|
||||||
void reshape(geom::box<float, 2> const & new_shape) override;
|
|
||||||
react::value<struct size_constraints> size_constraints() const override;
|
|
||||||
|
|
||||||
void set_child(std::unique_ptr<component> child) override;
|
|
||||||
std::unique_ptr<component> release_child() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
react::value<geom::vector<float, 2>> margin_;
|
|
||||||
react::source<react::value<struct size_constraints>> child_size_constraints_;
|
|
||||||
react::value<struct size_constraints> size_constraints_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/geom/box.hpp>
|
|
||||||
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
struct size_constraints
|
|
||||||
{
|
|
||||||
geom::box<float, 2> box;
|
|
||||||
|
|
||||||
static constexpr float infinity = std::numeric_limits<float>::infinity();
|
|
||||||
|
|
||||||
static size_constraints max();
|
|
||||||
};
|
|
||||||
|
|
||||||
size_constraints shift(size_constraints const & constraints, geom::vector<float, 2> const & delta);
|
|
||||||
size_constraints scale(size_constraints const & constraints, float factor, int dimension);
|
|
||||||
|
|
||||||
float min(size_constraints const & constraints, int dimension);
|
|
||||||
|
|
||||||
size_constraints intersect(size_constraints const & constraints1, size_constraints const & constraints2);
|
|
||||||
|
|
||||||
geom::interval<float> range(size_constraints const & constraints, int dimension);
|
|
||||||
|
|
||||||
size_constraints cut(size_constraints const & constraints, int dimension, geom::interval<float> const & range);
|
|
||||||
|
|
||||||
size_constraints sum_along(size_constraints const & constraints1, size_constraints const & constraints2, int dimension);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/impl/container.hpp>
|
|
||||||
#include <psemek/ui/stack_layout.hpp>
|
|
||||||
#include <psemek/react/value.hpp>
|
|
||||||
#include <psemek/react/source.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
struct stack_layout_base
|
|
||||||
: container
|
|
||||||
{
|
|
||||||
stack_layout_base();
|
|
||||||
|
|
||||||
void reshape(geom::box<float, 2> const & new_shape) override;
|
|
||||||
react::value<struct size_constraints> size_constraints() const override;
|
|
||||||
|
|
||||||
void set_children(std::vector<std::unique_ptr<component>> children) override;
|
|
||||||
std::vector<std::unique_ptr<component>> release_children() override;
|
|
||||||
|
|
||||||
void update(stack_layout const &)
|
|
||||||
{}
|
|
||||||
|
|
||||||
private:
|
|
||||||
react::source<std::vector<react::value<struct size_constraints>>> children_size_constraints_;
|
|
||||||
react::value<struct size_constraints> size_constraints_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/storage.hpp>
|
|
||||||
#include <psemek/ui/impl/single_container.hpp>
|
|
||||||
#include <psemek/react/source.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
struct storage_base
|
|
||||||
: single_container
|
|
||||||
{
|
|
||||||
storage_base();
|
|
||||||
|
|
||||||
void reshape(geom::box<float, 2> const & new_shape) override;
|
|
||||||
react::value<struct size_constraints> size_constraints() const override;
|
|
||||||
|
|
||||||
void set_child(std::unique_ptr<component> child) override;
|
|
||||||
std::unique_ptr<component> release_child() override;
|
|
||||||
|
|
||||||
void update(storage const & value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
util::hash_map<std::string, react::source<std::any>> values_;
|
|
||||||
react::source<react::value<struct size_constraints>> child_size_constraints_;
|
|
||||||
react::value<struct size_constraints> size_constraints_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace psemek::ui
|
|
||||||
{
|
|
||||||
|
|
||||||
using key = std::string;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/alignment.hpp>
|
|
||||||
#include <psemek/fonts/font_v2.hpp>
|
|
||||||
#include <psemek/gfx/color.hpp>
|
|
||||||
#include <psemek/react/value.hpp>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace psemek::ui
|
|
||||||
{
|
|
||||||
|
|
||||||
struct label
|
|
||||||
{
|
|
||||||
react::value<std::string> text = {};
|
|
||||||
react::value<fonts::font *> font = {};
|
|
||||||
react::value<gfx::color_rgba> color = gfx::color_rgba{0, 0, 0, 255};
|
|
||||||
react::value<halignment> halign = halignment::center;
|
|
||||||
react::value<valignment> valign = valignment::center;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/react/value.hpp>
|
|
||||||
#include <psemek/geom/vector.hpp>
|
|
||||||
|
|
||||||
#include <any>
|
|
||||||
|
|
||||||
namespace psemek::ui
|
|
||||||
{
|
|
||||||
|
|
||||||
struct move
|
|
||||||
{
|
|
||||||
react::value<std::any> child = {};
|
|
||||||
react::value<geom::vector<float, 2>> offset = geom::vector{0.f, 0.f};
|
|
||||||
react::value<float> animation_speed = 0.f;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/react/value.hpp>
|
|
||||||
#include <psemek/gfx/color.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui
|
|
||||||
{
|
|
||||||
|
|
||||||
struct rectangle
|
|
||||||
{
|
|
||||||
react::value<gfx::color_rgba> color = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/react/source.hpp>
|
|
||||||
#include <psemek/geom/box.hpp>
|
|
||||||
|
|
||||||
#include <any>
|
|
||||||
|
|
||||||
namespace psemek::ui
|
|
||||||
{
|
|
||||||
|
|
||||||
struct shape_reader
|
|
||||||
{
|
|
||||||
react::value<std::any> child = {};
|
|
||||||
react::source<geom::box<float, 2>> shape = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/react/value.hpp>
|
|
||||||
#include <psemek/react/source.hpp>
|
|
||||||
#include <psemek/geom/interval.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui
|
|
||||||
{
|
|
||||||
|
|
||||||
struct slider
|
|
||||||
{
|
|
||||||
react::value<geom::interval<int>> range = {};
|
|
||||||
react::source<int> value = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/key.hpp>
|
|
||||||
#include <psemek/react/value.hpp>
|
|
||||||
|
|
||||||
#include <any>
|
|
||||||
|
|
||||||
namespace psemek::ui
|
|
||||||
{
|
|
||||||
|
|
||||||
struct stack_layout
|
|
||||||
{
|
|
||||||
struct element
|
|
||||||
{
|
|
||||||
react::value<std::any> element;
|
|
||||||
std::optional<ui::key> key = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
react::value<std::vector<element>> children = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/react/source.hpp>
|
|
||||||
#include <psemek/geom/box.hpp>
|
|
||||||
#include <psemek/util/hash_table.hpp>
|
|
||||||
|
|
||||||
#include <any>
|
|
||||||
|
|
||||||
namespace psemek::ui
|
|
||||||
{
|
|
||||||
|
|
||||||
struct storage
|
|
||||||
{
|
|
||||||
react::value<std::any> child = {};
|
|
||||||
|
|
||||||
util::hash_map<std::string, react::source<std::any>> values = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
#include <psemek/ui/impl/aligned_base.hpp>
|
|
||||||
#include <psemek/react/join.hpp>
|
|
||||||
#include <psemek/react/map.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
size_constraints compute_size_constraints(size_constraints const & child_size_constraints)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
.box = {{
|
|
||||||
{child_size_constraints.box[0].min, size_constraints::infinity},
|
|
||||||
{child_size_constraints.box[1].min, size_constraints::infinity},
|
|
||||||
}}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
aligned_base::aligned_base()
|
|
||||||
: align_({halignment::center, valignment::center})
|
|
||||||
, child_size_constraints_(size_constraints::max())
|
|
||||||
, size_constraints_(react::map(compute_size_constraints, react::join(child_size_constraints_)))
|
|
||||||
{}
|
|
||||||
|
|
||||||
void aligned_base::reshape(geom::box<float, 2> const & new_shape)
|
|
||||||
{
|
|
||||||
single_container::reshape(new_shape);
|
|
||||||
|
|
||||||
auto [ halign, valign ] = **align_;
|
|
||||||
|
|
||||||
auto child_size_constraints = **child_size_constraints_;
|
|
||||||
|
|
||||||
if (auto child = this->child())
|
|
||||||
child->reshape({
|
|
||||||
align(new_shape[0], child_size_constraints.box[0].min, halign),
|
|
||||||
align(new_shape[1], child_size_constraints.box[1].min, valign),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
react::value<struct size_constraints> aligned_base::size_constraints() const
|
|
||||||
{
|
|
||||||
return size_constraints_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void aligned_base::set_child(std::unique_ptr<component> child)
|
|
||||||
{
|
|
||||||
if (child)
|
|
||||||
child_size_constraints_.set(child->size_constraints());
|
|
||||||
else
|
|
||||||
child_size_constraints_.set(size_constraints::max());
|
|
||||||
|
|
||||||
single_container::set_child(std::move(child));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<component> aligned_base::release_child()
|
|
||||||
{
|
|
||||||
child_size_constraints_.set(size_constraints::max());
|
|
||||||
|
|
||||||
return single_container::release_child();
|
|
||||||
}
|
|
||||||
|
|
||||||
void aligned_base::update(aligned const & value)
|
|
||||||
{
|
|
||||||
align_.set(react::map([](auto const & halign, auto const & valign){ return std::pair{halign, valign}; }, value.halign, value.valign));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,188 +0,0 @@
|
||||||
#include <psemek/ui/impl/box_layout_base.hpp>
|
|
||||||
#include <psemek/react/map.hpp>
|
|
||||||
#include <psemek/react/join.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
static constexpr box_layout::size_policy default_policy = box_layout::minimized{};
|
|
||||||
|
|
||||||
template <int Dimension>
|
|
||||||
size_constraints compute_size_constraints(std::vector<box_layout::size_policy> const & size_policies,
|
|
||||||
std::vector<size_constraints> const & children_size_constraints, float padding)
|
|
||||||
{
|
|
||||||
size_constraints minimized = size_constraints::max();
|
|
||||||
size_constraints weight_unit = minimized;
|
|
||||||
float weight_sum = 0.f;
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < std::min(size_policies.size(), children_size_constraints.size()); ++i)
|
|
||||||
{
|
|
||||||
if (std::get_if<box_layout::minimized>(&size_policies[i]))
|
|
||||||
{
|
|
||||||
minimized = sum_along(minimized, children_size_constraints[i], Dimension);
|
|
||||||
}
|
|
||||||
else if (auto weight = std::get_if<box_layout::weight>(&size_policies[i]))
|
|
||||||
{
|
|
||||||
weight_unit = intersect(weight_unit, scale(children_size_constraints[i], 1.f / weight->value, Dimension));
|
|
||||||
weight_sum += weight->value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto result = std::move(minimized);
|
|
||||||
|
|
||||||
if (weight_sum > 0.f)
|
|
||||||
result = sum_along(result, scale(weight_unit, weight_sum, Dimension), Dimension);
|
|
||||||
|
|
||||||
if (size_policies.size() > 0)
|
|
||||||
{
|
|
||||||
geom::vector shift_delta{0.f, 0.f};
|
|
||||||
shift_delta[Dimension] = padding * (size_policies.size() - 1);
|
|
||||||
result = shift(std::move(result), shift_delta);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <int Dimension>
|
|
||||||
std::vector<float> allocate(float total_size, std::vector<react::value<box_layout::size_policy>> const & size_policies,
|
|
||||||
util::span<std::unique_ptr<component> const> children, float padding)
|
|
||||||
{
|
|
||||||
std::vector<float> result(std::min(size_policies.size(), children.size()), 0.f);
|
|
||||||
|
|
||||||
if (!size_policies.empty())
|
|
||||||
total_size -= padding * (result.size() - 1);
|
|
||||||
|
|
||||||
float total_weight = 0.f;
|
|
||||||
|
|
||||||
// First, allocate minimized elements
|
|
||||||
// Also compute the total weight for weighted elements
|
|
||||||
for (std::size_t i = 0; i < result.size(); ++i)
|
|
||||||
{
|
|
||||||
if (!children[i])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto const policy = size_policies[i] ? *size_policies[i] : default_policy;
|
|
||||||
|
|
||||||
if (std::get_if<box_layout::minimized>(&policy))
|
|
||||||
{
|
|
||||||
result[i] = min(*children[i]->size_constraints(), Dimension);
|
|
||||||
total_size -= result[i];
|
|
||||||
}
|
|
||||||
else if (auto weight = std::get_if<box_layout::weight>(&policy))
|
|
||||||
{
|
|
||||||
total_weight += weight->value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next, allocate weighted elements
|
|
||||||
for (std::size_t i = 0; i < result.size(); ++i)
|
|
||||||
{
|
|
||||||
auto const policy = size_policies[i] ? *size_policies[i] : default_policy;
|
|
||||||
|
|
||||||
if (auto weight = std::get_if<box_layout::weight>(&policy))
|
|
||||||
{
|
|
||||||
result[i] = total_size * weight->value / total_weight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
template <int Dimension>
|
|
||||||
box_layout_base<Dimension>::box_layout_base()
|
|
||||||
: padding_(0.f)
|
|
||||||
, size_policies_(std::vector<react::value<box_layout::size_policy>>{})
|
|
||||||
, children_size_constraints_()
|
|
||||||
, size_constraints_(
|
|
||||||
react::map(
|
|
||||||
[](auto const & size_policies, auto const & children_size_constraints, auto const & padding){
|
|
||||||
return compute_size_constraints<Dimension>(size_policies, children_size_constraints, padding);
|
|
||||||
},
|
|
||||||
react::join(react::map(react::unpack_with_default(default_policy), react::join(size_policies_))),
|
|
||||||
react::join(react::map(react::unpack, children_size_constraints_)),
|
|
||||||
react::join(padding_)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{}
|
|
||||||
|
|
||||||
template <int Dimension>
|
|
||||||
void box_layout_base<Dimension>::reshape(geom::box<float, 2> const & new_shape)
|
|
||||||
{
|
|
||||||
container::reshape(new_shape);
|
|
||||||
|
|
||||||
if (children().empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto padding = **padding_;
|
|
||||||
|
|
||||||
auto sizes = allocate<Dimension>(new_shape[Dimension].length(), **size_policies_, children(), padding);
|
|
||||||
|
|
||||||
float pen = new_shape[Dimension].min;
|
|
||||||
for (std::size_t i = 0; i < children().size(); ++i)
|
|
||||||
{
|
|
||||||
if (children()[i])
|
|
||||||
{
|
|
||||||
geom::box<float, 2> child_shape;
|
|
||||||
child_shape[Dimension] = {pen, pen + sizes[i]};
|
|
||||||
child_shape[Dimension ^ 1] = new_shape[Dimension ^ 1];
|
|
||||||
children()[i]->reshape(child_shape);
|
|
||||||
}
|
|
||||||
|
|
||||||
pen += sizes[i];
|
|
||||||
pen += padding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <int Dimension>
|
|
||||||
react::value<size_constraints> box_layout_base<Dimension>::size_constraints() const
|
|
||||||
{
|
|
||||||
return size_constraints_;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <int Dimension>
|
|
||||||
void box_layout_base<Dimension>::set_children(std::vector<std::unique_ptr<component>> children)
|
|
||||||
{
|
|
||||||
container::set_children(std::move(children));
|
|
||||||
|
|
||||||
std::vector<react::value<struct size_constraints>> children_size_constraints;
|
|
||||||
for (auto const & child : this->children())
|
|
||||||
{
|
|
||||||
if (child)
|
|
||||||
children_size_constraints.push_back(child->size_constraints());
|
|
||||||
else
|
|
||||||
children_size_constraints.push_back(size_constraints::max());
|
|
||||||
}
|
|
||||||
children_size_constraints_.set(std::move(children_size_constraints));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <int Dimension>
|
|
||||||
std::vector<std::unique_ptr<component>> box_layout_base<Dimension>::release_children()
|
|
||||||
{
|
|
||||||
auto result = container::release_children();
|
|
||||||
children_size_constraints_.set({});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <int Dimension>
|
|
||||||
void box_layout_base<Dimension>::update(typename detail::box_layout_type<Dimension>::type const & box_layout)
|
|
||||||
{
|
|
||||||
size_policies_.set(react::map([](auto const & children){
|
|
||||||
std::vector<react::value<box_layout::size_policy>> size_policies;
|
|
||||||
size_policies.reserve(children.size());
|
|
||||||
for (auto const & child : children)
|
|
||||||
size_policies.push_back(child.policy);
|
|
||||||
return size_policies;
|
|
||||||
}, box_layout.children));
|
|
||||||
|
|
||||||
padding_.set(box_layout.padding);
|
|
||||||
}
|
|
||||||
|
|
||||||
template struct box_layout_base<0>;
|
|
||||||
template struct box_layout_base<1>;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
#include <psemek/ui/impl/button_base.hpp>
|
|
||||||
#include <psemek/geom/contains.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
button_base::button_base()
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool button_base::on_event(mouse_move_event const & event)
|
|
||||||
{
|
|
||||||
bool const mouseover = geom::contains(shape(), geom::cast<float>(event.position));
|
|
||||||
|
|
||||||
if (is_mouseover_ != mouseover)
|
|
||||||
{
|
|
||||||
is_mouseover_ = mouseover;
|
|
||||||
mouseover_.set(is_mouseover_);
|
|
||||||
on_mouseover_.set(is_mouseover_);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool button_base::on_event(mouse_button_event const & event)
|
|
||||||
{
|
|
||||||
bool const mousedown = event.button == app::mouse_button::left && event.down;
|
|
||||||
|
|
||||||
if (is_mouseover_ || !mousedown)
|
|
||||||
{
|
|
||||||
if (is_mousedown_ != mousedown)
|
|
||||||
{
|
|
||||||
is_mousedown_ = mousedown;
|
|
||||||
mousedown_.set(is_mousedown_);
|
|
||||||
on_mousedown_.set(is_mousedown_);
|
|
||||||
}
|
|
||||||
|
|
||||||
return is_mouseover_ && is_mousedown_;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void button_base::update(button const & value)
|
|
||||||
{
|
|
||||||
mouseover_ = value.mouseover;
|
|
||||||
mousedown_ = value.mousedown;
|
|
||||||
|
|
||||||
on_mouseover_ = value.on_mouseover;
|
|
||||||
on_mousedown_ = value.on_mousedown;
|
|
||||||
|
|
||||||
mouseover_.set(is_mouseover_);
|
|
||||||
mousedown_.set(is_mousedown_);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
#include <psemek/ui/impl/component.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
util::span<std::unique_ptr<component> const> component::children() const
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
geom::box<float, 2> const & component::shape() const
|
|
||||||
{
|
|
||||||
return shape_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void component::reshape(geom::box<float, 2> const & new_shape)
|
|
||||||
{
|
|
||||||
shape_ = new_shape;
|
|
||||||
}
|
|
||||||
|
|
||||||
react::value<size_constraints> component::size_constraints() const
|
|
||||||
{
|
|
||||||
static react::value<struct size_constraints> const result{size_constraints::max()};
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
#include <psemek/ui/impl/container.hpp>
|
|
||||||
#include <psemek/util/range.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
void container::set_children(std::vector<std::unique_ptr<component>> children)
|
|
||||||
{
|
|
||||||
children_ = std::move(children);
|
|
||||||
}
|
|
||||||
|
|
||||||
void container::set_children_token(util::signal<std::vector<std::any>>::subscription_token token)
|
|
||||||
{
|
|
||||||
children_token_ = std::move(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
void container::set_child_tokens(std::vector<util::signal<std::any>::subscription_token> tokens)
|
|
||||||
{
|
|
||||||
child_tokens_ = std::move(tokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
void container::set_child_keys(std::vector<std::optional<key>> keys)
|
|
||||||
{
|
|
||||||
child_keys_ = std::move(keys);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<component>> container::release_children()
|
|
||||||
{
|
|
||||||
return std::move(children_);
|
|
||||||
}
|
|
||||||
|
|
||||||
util::signal<>::subscription_token container::release_children_token()
|
|
||||||
{
|
|
||||||
return std::move(children_token_);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<util::signal<>::subscription_token> container::release_child_tokens()
|
|
||||||
{
|
|
||||||
return std::move(child_tokens_);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::optional<key>> container::release_child_keys()
|
|
||||||
{
|
|
||||||
return std::move(child_keys_);
|
|
||||||
}
|
|
||||||
|
|
||||||
util::span<std::unique_ptr<component> const> container::children() const
|
|
||||||
{
|
|
||||||
return children_;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,180 +0,0 @@
|
||||||
#include <psemek/ui/impl/controller.hpp>
|
|
||||||
#include <psemek/ui/impl/event_state.hpp>
|
|
||||||
#include <psemek/async/event_loop.hpp>
|
|
||||||
#include <psemek/util/range.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
struct controller::impl
|
|
||||||
{
|
|
||||||
component_factory & factory;
|
|
||||||
event_state state;
|
|
||||||
|
|
||||||
util::signal<>::subscription_token root_token;
|
|
||||||
std::unique_ptr<ui::impl::component> root;
|
|
||||||
|
|
||||||
async::event_loop delayed_queue;
|
|
||||||
|
|
||||||
impl(component_factory & factory)
|
|
||||||
: factory(factory)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void set_ui(react::value<std::any> ui)
|
|
||||||
{
|
|
||||||
root_token = ui.subscribe([this](std::any const & value)
|
|
||||||
{
|
|
||||||
root = factory.reconciliate(std::move(root), value, state, delayed_queue, [this]{ delayed_queue.post([this]{ reshape(); }); });
|
|
||||||
}, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void reshape()
|
|
||||||
{
|
|
||||||
if (!root)
|
|
||||||
return;
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
apply(state, event);
|
|
||||||
delayed_queue.post([this]{ reshape(); });
|
|
||||||
return on_event_impl(event, root.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Event>
|
|
||||||
bool on_event_impl(Event const & event, component * element)
|
|
||||||
{
|
|
||||||
if (!element)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (auto const & child : util::reversed(element->children()))
|
|
||||||
if (on_event_impl(event, child.get()))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return element->on_event(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void update(float dt)
|
|
||||||
{
|
|
||||||
delayed_queue.pump();
|
|
||||||
animate_impl(dt, root.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
void animate_impl(float dt, component * element)
|
|
||||||
{
|
|
||||||
if (!element)
|
|
||||||
return;
|
|
||||||
|
|
||||||
element->animate(dt);
|
|
||||||
for (auto const & child : element->children())
|
|
||||||
animate_impl(dt, child.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw(renderer & renderer)
|
|
||||||
{
|
|
||||||
draw_impl(renderer, root.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw_impl(renderer & renderer, component * element)
|
|
||||||
{
|
|
||||||
if (!element)
|
|
||||||
return;
|
|
||||||
|
|
||||||
element->draw(renderer);
|
|
||||||
|
|
||||||
for (auto const & child : element->children())
|
|
||||||
draw_impl(renderer, child.get());
|
|
||||||
|
|
||||||
element->post_draw(renderer);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> cursor()
|
|
||||||
{
|
|
||||||
return cursor_impl(root.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> cursor_impl(component * element)
|
|
||||||
{
|
|
||||||
if (!element)
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
for (auto const & child : util::reversed(element->children()))
|
|
||||||
if (auto cursor = cursor_impl(child.get()))
|
|
||||||
return cursor;
|
|
||||||
|
|
||||||
return element->cursor();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
controller::controller(component_factory & factory)
|
|
||||||
: pimpl_(make_impl(factory))
|
|
||||||
{}
|
|
||||||
|
|
||||||
controller::~controller() = default;
|
|
||||||
|
|
||||||
void controller::set_ui(react::value<std::any> ui)
|
|
||||||
{
|
|
||||||
impl().set_ui(std::move(ui));
|
|
||||||
}
|
|
||||||
|
|
||||||
component * controller::root()
|
|
||||||
{
|
|
||||||
return impl().root.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool controller::on_event(resize_event const & event)
|
|
||||||
{
|
|
||||||
return impl().on_event(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool controller::on_event(mouse_move_event const & event)
|
|
||||||
{
|
|
||||||
return impl().on_event(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool controller::on_event(mouse_wheel_event const & event)
|
|
||||||
{
|
|
||||||
return impl().on_event(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool controller::on_event(mouse_button_event const & event)
|
|
||||||
{
|
|
||||||
return impl().on_event(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool controller::on_event(key_event const & event)
|
|
||||||
{
|
|
||||||
return impl().on_event(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
event_state const & controller::state()
|
|
||||||
{
|
|
||||||
return impl().state;
|
|
||||||
}
|
|
||||||
|
|
||||||
void controller::update(float dt)
|
|
||||||
{
|
|
||||||
impl().update(dt);
|
|
||||||
}
|
|
||||||
|
|
||||||
void controller::draw(renderer & renderer)
|
|
||||||
{
|
|
||||||
impl().draw(renderer);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::string> controller::cursor()
|
|
||||||
{
|
|
||||||
return impl().cursor();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
#include <psemek/ui/impl/default_component_factory.hpp>
|
|
||||||
#include <psemek/ui/stack_layout.hpp>
|
|
||||||
#include <psemek/ui/box_layout.hpp>
|
|
||||||
#include <psemek/ui/aligned.hpp>
|
|
||||||
#include <psemek/ui/fixed_size.hpp>
|
|
||||||
#include <psemek/ui/button.hpp>
|
|
||||||
#include <psemek/ui/impl/stack_layout_base.hpp>
|
|
||||||
#include <psemek/ui/impl/box_layout_base.hpp>
|
|
||||||
#include <psemek/ui/impl/aligned_base.hpp>
|
|
||||||
#include <psemek/ui/impl/fixed_size_base.hpp>
|
|
||||||
#include <psemek/ui/impl/frame_base.hpp>
|
|
||||||
#include <psemek/ui/impl/extend_base.hpp>
|
|
||||||
#include <psemek/ui/impl/move_base.hpp>
|
|
||||||
#include <psemek/ui/impl/button_base.hpp>
|
|
||||||
#include <psemek/ui/impl/label_base.hpp>
|
|
||||||
#include <psemek/ui/impl/floating_base.hpp>
|
|
||||||
#include <psemek/ui/impl/shape_reader_base.hpp>
|
|
||||||
#include <psemek/ui/impl/storage_base.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
default_component_factory::default_component_factory()
|
|
||||||
{
|
|
||||||
register_type<stack_layout, impl::stack_layout_base>();
|
|
||||||
register_type<box_layout::horizontal, impl::box_layout_base<0>>();
|
|
||||||
register_type<box_layout::vertical, impl::box_layout_base<1>>();
|
|
||||||
register_type<aligned, impl::aligned_base>();
|
|
||||||
register_type<fixed_size, impl::fixed_size_base>();
|
|
||||||
register_type<fixed_width, impl::fixed_size_base>();
|
|
||||||
register_type<fixed_height, impl::fixed_size_base>();
|
|
||||||
register_type<frame, impl::frame_base>();
|
|
||||||
register_type<extend, impl::extend_base>();
|
|
||||||
register_type<move, impl::move_base>();
|
|
||||||
register_type<button, impl::button_base>();
|
|
||||||
register_type<label, impl::label_base>();
|
|
||||||
register_type<floating, impl::floating_base>();
|
|
||||||
register_type<storage, impl::storage_base>();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
#include <psemek/ui/impl/extend_base.hpp>
|
|
||||||
#include <psemek/react/join.hpp>
|
|
||||||
#include <psemek/react/map.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
geom::box<float, 2> compute_child_shape(geom::box<float, 2> shape, float left, float right, float top, float bottom)
|
|
||||||
{
|
|
||||||
shape[0].min -= left;
|
|
||||||
shape[0].max += right;
|
|
||||||
shape[1].min -= top;
|
|
||||||
shape[1].max += bottom;
|
|
||||||
return shape;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extend_base::extend_base()
|
|
||||||
: left_(0.f)
|
|
||||||
, right_(0.f)
|
|
||||||
, top_(0.f)
|
|
||||||
, bottom_(0.f)
|
|
||||||
, child_size_constraints_(size_constraints::max())
|
|
||||||
, size_constraints_(react::join(child_size_constraints_))
|
|
||||||
{}
|
|
||||||
|
|
||||||
void extend_base::reshape(geom::box<float, 2> const & new_shape)
|
|
||||||
{
|
|
||||||
single_container::reshape(new_shape);
|
|
||||||
|
|
||||||
if (auto child = this->child())
|
|
||||||
child->reshape(compute_child_shape(new_shape, **left_, **right_, **top_, **bottom_));
|
|
||||||
}
|
|
||||||
|
|
||||||
react::value<struct size_constraints> extend_base::size_constraints() const
|
|
||||||
{
|
|
||||||
return size_constraints_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void extend_base::set_child(std::unique_ptr<component> child)
|
|
||||||
{
|
|
||||||
if (child)
|
|
||||||
child_size_constraints_.set(child->size_constraints());
|
|
||||||
else
|
|
||||||
child_size_constraints_.set(size_constraints::max());
|
|
||||||
single_container::set_child(std::move(child));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<component> extend_base::release_child()
|
|
||||||
{
|
|
||||||
child_size_constraints_.set(size_constraints::max());
|
|
||||||
return single_container::release_child();
|
|
||||||
}
|
|
||||||
|
|
||||||
void extend_base::update(extend const & value)
|
|
||||||
{
|
|
||||||
left_.set(value.left);
|
|
||||||
right_.set(value.right);
|
|
||||||
top_.set(value.top);
|
|
||||||
bottom_.set(value.bottom);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
#include <psemek/ui/impl/fixed_size_base.hpp>
|
|
||||||
#include <psemek/ui/alignment.hpp>
|
|
||||||
#include <psemek/react/join.hpp>
|
|
||||||
#include <psemek/react/map.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
size_constraints compute_size_constraints(geom::vector<std::optional<float>, 2> const & size)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
.box = {{
|
|
||||||
size[0] ? geom::interval<float>::singleton(*(size[0])) : geom::interval<float>(0.f, size_constraints::infinity),
|
|
||||||
size[1] ? geom::interval<float>::singleton(*(size[1])) : geom::interval<float>(0.f, size_constraints::infinity),
|
|
||||||
}},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
geom::box<float, 2> compute_shape(geom::box<float, 2> const & shape, geom::vector<std::optional<float>, 2> const & size)
|
|
||||||
{
|
|
||||||
return {{
|
|
||||||
size[0] ? align(shape[0], *(size[0]), halignment::center) : shape[0],
|
|
||||||
size[1] ? align(shape[1], *(size[1]), valignment::center) : shape[1],
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed_size_base::fixed_size_base()
|
|
||||||
: size_({0.f, 0.f})
|
|
||||||
, size_constraints_(react::map(compute_size_constraints, react::join(size_)))
|
|
||||||
{}
|
|
||||||
|
|
||||||
void fixed_size_base::reshape(geom::box<float, 2> const & new_shape)
|
|
||||||
{
|
|
||||||
single_container::reshape(new_shape);
|
|
||||||
|
|
||||||
if (auto child = this->child())
|
|
||||||
child->reshape(compute_shape(new_shape, **size_));
|
|
||||||
}
|
|
||||||
|
|
||||||
react::value<struct size_constraints> fixed_size_base::size_constraints() const
|
|
||||||
{
|
|
||||||
return size_constraints_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fixed_size_base::update(fixed_size const & value)
|
|
||||||
{
|
|
||||||
size_.set(react::map([](geom::vector<float, 2> const & size){ return geom::vector<std::optional<float>, 2>{size[0], size[1]}; }, value.size));
|
|
||||||
}
|
|
||||||
|
|
||||||
void fixed_size_base::update(fixed_width const & value)
|
|
||||||
{
|
|
||||||
size_.set(react::map([](float width){ return geom::vector<std::optional<float>, 2>{width, std::nullopt}; }, value.width));
|
|
||||||
}
|
|
||||||
|
|
||||||
void fixed_size_base::update(fixed_height const & value)
|
|
||||||
{
|
|
||||||
size_.set(react::map([](float height){ return geom::vector<std::optional<float>, 2>{std::nullopt, height}; }, value.height));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
#include <psemek/ui/impl/floating_base.hpp>
|
|
||||||
#include <psemek/ui/alignment.hpp>
|
|
||||||
#include <psemek/react/join.hpp>
|
|
||||||
#include <psemek/react/map.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
geom::box<float, 2> compute_child_shape(geom::box<float, 2> const & size_constraints, geom::point<float, 2> const & anchor, halignment halign, valignment valign)
|
|
||||||
{
|
|
||||||
geom::vector size{size_constraints[0].min, size_constraints[1].min};
|
|
||||||
|
|
||||||
geom::box<float, 2> result;
|
|
||||||
result[0] = geom::interval{- size[0], 0.f} + (1.f - lerp_factor(halign)) * size[0] + anchor[0];
|
|
||||||
result[1] = geom::interval{- size[1], 0.f} + (1.f - lerp_factor(valign)) * size[1] + anchor[1];
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
floating_base::floating_base()
|
|
||||||
: size_constraints_(impl::size_constraints::max())
|
|
||||||
{}
|
|
||||||
|
|
||||||
void floating_base::reshape(geom::box<float, 2> const & new_shape)
|
|
||||||
{
|
|
||||||
single_container::reshape(new_shape);
|
|
||||||
|
|
||||||
if (auto child = this->child())
|
|
||||||
child->reshape(compute_child_shape((*(child->size_constraints())).box, *anchor_, *halign_, *valign_));
|
|
||||||
}
|
|
||||||
|
|
||||||
react::value<struct size_constraints> floating_base::size_constraints() const
|
|
||||||
{
|
|
||||||
return size_constraints_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void floating_base::update(floating const & value)
|
|
||||||
{
|
|
||||||
anchor_ = value.anchor;
|
|
||||||
halign_ = value.halign;
|
|
||||||
valign_ = value.valign;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
#include <psemek/ui/impl/frame_base.hpp>
|
|
||||||
#include <psemek/react/join.hpp>
|
|
||||||
#include <psemek/react/map.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
size_constraints compute_size_constraints(size_constraints const & child_size_constraints, float margin)
|
|
||||||
{
|
|
||||||
return { child_size_constraints.box + geom::vector{2.f * margin, 2.f * margin} };
|
|
||||||
}
|
|
||||||
|
|
||||||
geom::box<float, 2> compute_child_shape(geom::box<float, 2> const & shape, float margin)
|
|
||||||
{
|
|
||||||
return geom::expand(shape, -margin);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
frame_base::frame_base()
|
|
||||||
: margin_(0.f)
|
|
||||||
, child_size_constraints_(size_constraints::max())
|
|
||||||
, size_constraints_(react::map(compute_size_constraints, react::join(child_size_constraints_), react::join(margin_)))
|
|
||||||
{}
|
|
||||||
|
|
||||||
void frame_base::reshape(geom::box<float, 2> const & new_shape)
|
|
||||||
{
|
|
||||||
single_container::reshape(new_shape);
|
|
||||||
|
|
||||||
if (auto child = this->child())
|
|
||||||
child->reshape(compute_child_shape(new_shape, **margin_));
|
|
||||||
}
|
|
||||||
|
|
||||||
react::value<struct size_constraints> frame_base::size_constraints() const
|
|
||||||
{
|
|
||||||
return size_constraints_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void frame_base::set_child(std::unique_ptr<component> child)
|
|
||||||
{
|
|
||||||
if (child)
|
|
||||||
child_size_constraints_.set(child->size_constraints());
|
|
||||||
else
|
|
||||||
child_size_constraints_.set(size_constraints::max());
|
|
||||||
single_container::set_child(std::move(child));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<component> frame_base::release_child()
|
|
||||||
{
|
|
||||||
child_size_constraints_.set(size_constraints::max());
|
|
||||||
return single_container::release_child();
|
|
||||||
}
|
|
||||||
|
|
||||||
void frame_base::update(frame const & value)
|
|
||||||
{
|
|
||||||
margin_.set(value.margin);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
#include <psemek/ui/impl/label_base.hpp>
|
|
||||||
#include <psemek/react/map.hpp>
|
|
||||||
#include <psemek/react/join.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
label_base::label_base()
|
|
||||||
: font_(react::value<fonts::font *>(nullptr))
|
|
||||||
, glyphs_(react::value<std::vector<fonts::shaped_glyph>>(std::vector<fonts::shaped_glyph>{}))
|
|
||||||
, size_(react::map([](std::vector<fonts::shaped_glyph> const & glyphs, fonts::font * font){
|
|
||||||
if (!font)
|
|
||||||
return geom::vector{0.f, 0.f};
|
|
||||||
geom::box<float, 2> bbox;
|
|
||||||
for (auto const & glyph : glyphs)
|
|
||||||
bbox |= glyph.position;
|
|
||||||
return geom::vector{bbox[0].length(), font->size()[1]};
|
|
||||||
}, react::join(glyphs_), react::join(font_)))
|
|
||||||
, size_constraints_(react::map([](geom::vector<float, 2> const & size){
|
|
||||||
geom::box<float, 2> result;
|
|
||||||
result[0].min = size[0];
|
|
||||||
result[0].max = size_constraints::infinity;
|
|
||||||
result[1].min = size[1];
|
|
||||||
result[1].max = size_constraints::infinity;
|
|
||||||
return impl::size_constraints{result};
|
|
||||||
}, size_))
|
|
||||||
{}
|
|
||||||
|
|
||||||
react::value<size_constraints> label_base::size_constraints() const
|
|
||||||
{
|
|
||||||
return size_constraints_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void label_base::draw(renderer & renderer)
|
|
||||||
{
|
|
||||||
if (!glyphs_ || !*glyphs_)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto const size = *size_;
|
|
||||||
auto const shape = this->shape();
|
|
||||||
|
|
||||||
geom::vector<float, 2> origin;
|
|
||||||
origin[0] = std::round(geom::lerp(shape[0].min, shape[0].max - size[0], lerp_factor(*halign_)));
|
|
||||||
origin[1] = std::round(geom::lerp(shape[1].min, shape[1].max - size[1], lerp_factor(*valign_)));
|
|
||||||
origin[1] += std::round((size[1] + (**font_)->xheight()) / 2.f);
|
|
||||||
|
|
||||||
for (auto const & glyph : **glyphs_)
|
|
||||||
renderer.draw_glyph(*glyph.texture, glyph.position + origin, glyph.texcoords, *color_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void label_base::update(label const & value)
|
|
||||||
{
|
|
||||||
font_.set(value.font);
|
|
||||||
glyphs_.set(react::map([](std::string const & str, fonts::font * font) -> std::vector<fonts::shaped_glyph> {
|
|
||||||
if (!font)
|
|
||||||
return {};
|
|
||||||
return font->shape(str, {});
|
|
||||||
}, value.text, value.font));
|
|
||||||
color_ = value.color;
|
|
||||||
halign_ = value.halign;
|
|
||||||
valign_ = value.valign;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
#include <psemek/ui/impl/move_base.hpp>
|
|
||||||
#include <psemek/react/join.hpp>
|
|
||||||
#include <psemek/react/map.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
geom::box<float, 2> compute_child_shape(geom::box<float, 2> const & shape, geom::vector<float, 2> const & offset)
|
|
||||||
{
|
|
||||||
return shape + offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
move_base::move_base()
|
|
||||||
: offset_({0.f, 0.f})
|
|
||||||
, animation_speed_(0.f)
|
|
||||||
, child_size_constraints_(size_constraints::max())
|
|
||||||
, size_constraints_(react::join(child_size_constraints_))
|
|
||||||
, current_offset_{0.f, 0.f}
|
|
||||||
{}
|
|
||||||
|
|
||||||
void move_base::reshape(geom::box<float, 2> const & new_shape)
|
|
||||||
{
|
|
||||||
single_container::reshape(new_shape);
|
|
||||||
|
|
||||||
if (auto child = this->child())
|
|
||||||
child->reshape(compute_child_shape(new_shape, current_offset_));
|
|
||||||
}
|
|
||||||
|
|
||||||
react::value<struct size_constraints> move_base::size_constraints() const
|
|
||||||
{
|
|
||||||
return size_constraints_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void move_base::set_child(std::unique_ptr<component> child)
|
|
||||||
{
|
|
||||||
if (child)
|
|
||||||
child_size_constraints_.set(child->size_constraints());
|
|
||||||
else
|
|
||||||
child_size_constraints_.set(size_constraints::max());
|
|
||||||
single_container::set_child(std::move(child));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<component> move_base::release_child()
|
|
||||||
{
|
|
||||||
child_size_constraints_.set(size_constraints::max());
|
|
||||||
return single_container::release_child();
|
|
||||||
}
|
|
||||||
|
|
||||||
void move_base::animate(float dt)
|
|
||||||
{
|
|
||||||
if (*animation_speed_ == 0.f)
|
|
||||||
{
|
|
||||||
if (current_offset_ != *offset_)
|
|
||||||
{
|
|
||||||
current_offset_ = *offset_;
|
|
||||||
reshape(shape());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto factor = - std::expm1(- dt * (*animation_speed_));
|
|
||||||
auto new_offset = geom::lerp(current_offset_, *offset_, factor);
|
|
||||||
if (geom::length(current_offset_ - new_offset) < 1.f / 16.f)
|
|
||||||
new_offset = *offset_;
|
|
||||||
|
|
||||||
if (current_offset_ != *offset_)
|
|
||||||
{
|
|
||||||
current_offset_ = new_offset;
|
|
||||||
reshape(shape());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void move_base::update(move const & value)
|
|
||||||
{
|
|
||||||
offset_ = value.offset;
|
|
||||||
animation_speed_ = value.animation_speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
#include <psemek/ui/impl/shape_reader_base.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
shape_reader_base::shape_reader_base()
|
|
||||||
: shape_(geom::box<float, 2>{{{0.f, 0.f}, {0.f, 0.f}}})
|
|
||||||
{}
|
|
||||||
|
|
||||||
void shape_reader_base::reshape(geom::box<float, 2> const & new_shape)
|
|
||||||
{
|
|
||||||
single_container::reshape(new_shape);
|
|
||||||
|
|
||||||
if (auto child = this->child())
|
|
||||||
child->reshape(new_shape);
|
|
||||||
|
|
||||||
shape_.set(new_shape);
|
|
||||||
}
|
|
||||||
|
|
||||||
react::value<struct size_constraints> shape_reader_base::size_constraints() const
|
|
||||||
{
|
|
||||||
if (auto child = this->child())
|
|
||||||
return child->size_constraints();
|
|
||||||
|
|
||||||
return size_constraints::max();
|
|
||||||
}
|
|
||||||
|
|
||||||
void shape_reader_base::update(shape_reader const & value)
|
|
||||||
{
|
|
||||||
shape_ = value.shape;
|
|
||||||
|
|
||||||
shape_.set(shape());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
#include <psemek/ui/impl/single_container.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
util::span<std::unique_ptr<component> const> single_container::children() const
|
|
||||||
{
|
|
||||||
return {&child_, 1};
|
|
||||||
}
|
|
||||||
|
|
||||||
void single_container::set_child(std::unique_ptr<component> child)
|
|
||||||
{
|
|
||||||
child_ = std::move(child);
|
|
||||||
}
|
|
||||||
|
|
||||||
void single_container::set_child_token(util::signal<>::subscription_token token)
|
|
||||||
{
|
|
||||||
child_token_ = std::move(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
component * single_container::child() const
|
|
||||||
{
|
|
||||||
return child_.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<component> single_container::release_child()
|
|
||||||
{
|
|
||||||
return std::move(child_);
|
|
||||||
}
|
|
||||||
|
|
||||||
util::signal<>::subscription_token single_container::release_child_token()
|
|
||||||
{
|
|
||||||
return std::move(child_token_);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
#include <psemek/ui/impl/single_container_base.hpp>
|
|
||||||
#include <psemek/react/map.hpp>
|
|
||||||
#include <psemek/react/join.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
single_container_base::single_container_base(react::value<geom::vector<float, 2>> margin)
|
|
||||||
: margin_(margin)
|
|
||||||
, child_size_constraints_(size_constraints::max())
|
|
||||||
, size_constraints_(react::map([](struct size_constraints const & child_constraints, geom::vector<float, 2> const & margin){
|
|
||||||
return shift(child_constraints, margin);
|
|
||||||
}, react::join(child_size_constraints_), margin))
|
|
||||||
{}
|
|
||||||
|
|
||||||
void single_container_base::reshape(geom::box<float, 2> const & new_shape)
|
|
||||||
{
|
|
||||||
single_container::reshape(new_shape);
|
|
||||||
|
|
||||||
if (child())
|
|
||||||
child()->reshape(geom::shrink(new_shape, *margin_));
|
|
||||||
}
|
|
||||||
|
|
||||||
react::value<size_constraints> single_container_base::size_constraints() const
|
|
||||||
{
|
|
||||||
return size_constraints_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void single_container_base::set_child(std::unique_ptr<component> child)
|
|
||||||
{
|
|
||||||
single_container::set_child(std::move(child));
|
|
||||||
child_size_constraints_.set(this->child() ? this->child()->size_constraints() : size_constraints::max());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<component> single_container_base::release_child()
|
|
||||||
{
|
|
||||||
auto child = single_container::release_child();
|
|
||||||
child_size_constraints_.set(size_constraints::max());
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
#include <psemek/ui/impl/size_constraints.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
size_constraints size_constraints::max()
|
|
||||||
{
|
|
||||||
return
|
|
||||||
{{{
|
|
||||||
{0.f, infinity},
|
|
||||||
{0.f, infinity},
|
|
||||||
}}};
|
|
||||||
}
|
|
||||||
|
|
||||||
size_constraints shift(size_constraints const & constraints, geom::vector<float, 2> const & delta)
|
|
||||||
{
|
|
||||||
return {constraints.box + delta};
|
|
||||||
}
|
|
||||||
|
|
||||||
size_constraints scale(size_constraints const & constraints, float factor, int dimension)
|
|
||||||
{
|
|
||||||
auto result = constraints;
|
|
||||||
result.box[dimension].min *= factor;
|
|
||||||
result.box[dimension].max *= factor;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
float min(size_constraints const & constraints, int dimension)
|
|
||||||
{
|
|
||||||
return constraints.box[dimension].min;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_constraints intersect(size_constraints const & constraints1, size_constraints const & constraints2)
|
|
||||||
{
|
|
||||||
return {constraints1.box & constraints2.box};
|
|
||||||
}
|
|
||||||
|
|
||||||
geom::interval<float> range(size_constraints const & constraints, int dimension)
|
|
||||||
{
|
|
||||||
return constraints.box[dimension];
|
|
||||||
}
|
|
||||||
|
|
||||||
size_constraints cut(size_constraints const & constraints, int dimension, geom::interval<float> const & range)
|
|
||||||
{
|
|
||||||
size_constraints result = constraints;
|
|
||||||
result.box[dimension] &= range;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_constraints sum_along(size_constraints const & constraints1, size_constraints const & constraints2, int dimension)
|
|
||||||
{
|
|
||||||
size_constraints result;
|
|
||||||
result.box[dimension].min = constraints1.box[dimension].min + constraints2.box[dimension].min;
|
|
||||||
result.box[dimension].max = constraints1.box[dimension].max + constraints2.box[dimension].max;
|
|
||||||
result.box[dimension ^ 1] = constraints1.box[dimension ^ 1] & constraints2.box[dimension ^ 1];
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
#include <psemek/ui/impl/stack_layout_base.hpp>
|
|
||||||
#include <psemek/react/join.hpp>
|
|
||||||
#include <psemek/react/map.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
size_constraints compute_size_constraints(std::vector<size_constraints> const & children_size_constraints)
|
|
||||||
{
|
|
||||||
size_constraints result = size_constraints::max();
|
|
||||||
for (auto const & constraints : children_size_constraints)
|
|
||||||
result = intersect(result, constraints);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
stack_layout_base::stack_layout_base()
|
|
||||||
: size_constraints_(react::map([](auto const & children_size_constraints){
|
|
||||||
return compute_size_constraints(children_size_constraints);
|
|
||||||
}, react::join(react::map(react::unpack, children_size_constraints_))))
|
|
||||||
{}
|
|
||||||
|
|
||||||
void stack_layout_base::reshape(geom::box<float, 2> const & new_shape)
|
|
||||||
{
|
|
||||||
container::reshape(new_shape);
|
|
||||||
|
|
||||||
for (auto const & child : children())
|
|
||||||
if (child)
|
|
||||||
child->reshape(new_shape);
|
|
||||||
}
|
|
||||||
|
|
||||||
react::value<size_constraints> stack_layout_base::size_constraints() const
|
|
||||||
{
|
|
||||||
return size_constraints_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void stack_layout_base::set_children(std::vector<std::unique_ptr<component>> children)
|
|
||||||
{
|
|
||||||
container::set_children(std::move(children));
|
|
||||||
std::vector<react::value<struct size_constraints>> children_size_constraints;
|
|
||||||
for (auto const & child : this->children())
|
|
||||||
{
|
|
||||||
if (child)
|
|
||||||
children_size_constraints.push_back(child->size_constraints());
|
|
||||||
else
|
|
||||||
children_size_constraints.push_back(size_constraints::max());
|
|
||||||
}
|
|
||||||
children_size_constraints_.set(std::move(children_size_constraints));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<component>> stack_layout_base::release_children()
|
|
||||||
{
|
|
||||||
auto result = container::release_children();
|
|
||||||
children_size_constraints_.set({});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
#include <psemek/ui/impl/storage_base.hpp>
|
|
||||||
|
|
||||||
#include <psemek/react/join.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
storage_base::storage_base()
|
|
||||||
: child_size_constraints_(size_constraints::max())
|
|
||||||
, size_constraints_(react::join(child_size_constraints_))
|
|
||||||
{}
|
|
||||||
|
|
||||||
void storage_base::reshape(geom::box<float, 2> const & new_shape)
|
|
||||||
{
|
|
||||||
single_container::reshape(new_shape);
|
|
||||||
|
|
||||||
if (auto child = this->child())
|
|
||||||
child->reshape(new_shape);
|
|
||||||
}
|
|
||||||
|
|
||||||
react::value<struct size_constraints> storage_base::size_constraints() const
|
|
||||||
{
|
|
||||||
if (auto child = this->child())
|
|
||||||
return child->size_constraints();
|
|
||||||
|
|
||||||
return impl::size_constraints::max();
|
|
||||||
}
|
|
||||||
|
|
||||||
void storage_base::set_child(std::unique_ptr<component> child)
|
|
||||||
{
|
|
||||||
if (child)
|
|
||||||
child_size_constraints_.set(child->size_constraints());
|
|
||||||
else
|
|
||||||
child_size_constraints_.set(size_constraints::max());
|
|
||||||
single_container::set_child(std::move(child));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<component> storage_base::release_child()
|
|
||||||
{
|
|
||||||
child_size_constraints_.set(size_constraints::max());
|
|
||||||
return single_container::release_child();
|
|
||||||
}
|
|
||||||
|
|
||||||
void storage_base::update(storage const & value)
|
|
||||||
{
|
|
||||||
util::hash_map<std::string, react::source<std::any>> new_values;
|
|
||||||
|
|
||||||
for (auto const & p : value.values)
|
|
||||||
{
|
|
||||||
auto & v = (new_values[p.first] = p.second);
|
|
||||||
|
|
||||||
if (auto it = values_.find(p.first); it != values_.end())
|
|
||||||
v.set(*(it->second));
|
|
||||||
}
|
|
||||||
|
|
||||||
values_ = std::move(new_values);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
#include <psemek/test/test.hpp>
|
|
||||||
|
|
||||||
#include <psemek/ui/impl/component_factory_base.hpp>
|
|
||||||
#include <psemek/ui/impl/single_container_base.hpp>
|
|
||||||
#include <psemek/ui/impl/stack_layout_base.hpp>
|
|
||||||
#include <psemek/ui/impl/box_layout_base.hpp>
|
|
||||||
#include <psemek/ui/rectangle.hpp>
|
|
||||||
|
|
||||||
#include <psemek/react/source.hpp>
|
|
||||||
|
|
||||||
using namespace psemek;
|
|
||||||
using namespace psemek::ui;
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
constexpr float layout_margin = 5.f;
|
|
||||||
|
|
||||||
struct rectangle_impl
|
|
||||||
: impl::component
|
|
||||||
{
|
|
||||||
void update(rectangle const &)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct test_component_factory
|
|
||||||
: impl::component_factory_base
|
|
||||||
{
|
|
||||||
test_component_factory()
|
|
||||||
{
|
|
||||||
register_type<rectangle, rectangle_impl>();
|
|
||||||
register_type<stack_layout, impl::stack_layout_base>();
|
|
||||||
register_type<box_layout::horizontal, impl::box_layout_base<0>>([]{ return std::make_unique<impl::box_layout_base<0>>(layout_margin); });
|
|
||||||
register_type<box_layout::vertical, impl::box_layout_base<1>>([]{ return std::make_unique<impl::box_layout_base<1>>(layout_margin); });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,364 +0,0 @@
|
||||||
#include <psemek/test/test.hpp>
|
|
||||||
|
|
||||||
#include <psemek/ui/impl/component_factory_base.hpp>
|
|
||||||
#include <psemek/ui/label.hpp>
|
|
||||||
#include <psemek/ui/rectangle.hpp>
|
|
||||||
#include <psemek/ui/button.hpp>
|
|
||||||
#include <psemek/ui/stack_layout.hpp>
|
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
using namespace psemek;
|
|
||||||
using namespace psemek::ui;
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
struct rectangle_impl
|
|
||||||
: impl::component
|
|
||||||
{
|
|
||||||
void update(rectangle const &)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct label_impl
|
|
||||||
: impl::component
|
|
||||||
{
|
|
||||||
void update(label const &)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct button_impl
|
|
||||||
: impl::single_container
|
|
||||||
{
|
|
||||||
void update(button const &)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct stack_layout_impl
|
|
||||||
: impl::container
|
|
||||||
{
|
|
||||||
void update(stack_layout const &)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct test_component_factory
|
|
||||||
: impl::component_factory_base
|
|
||||||
{
|
|
||||||
int rectangle_count = 0;
|
|
||||||
int label_count = 0;
|
|
||||||
int button_count = 0;
|
|
||||||
int stack_layout_count = 0;
|
|
||||||
|
|
||||||
test_component_factory()
|
|
||||||
{
|
|
||||||
register_type<rectangle, rectangle_impl>([this]{ ++rectangle_count; return std::make_unique<rectangle_impl>(); });
|
|
||||||
register_type<label, label_impl>([this]{ ++label_count; return std::make_unique<label_impl>(); });
|
|
||||||
register_type<button, button_impl>([this]{ ++button_count; return std::make_unique<button_impl>(); });
|
|
||||||
register_type<stack_layout, stack_layout_impl>([this]{ ++stack_layout_count; return std::make_unique<stack_layout_impl>(); });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct check_children_helper
|
|
||||||
{
|
|
||||||
impl::component & component;
|
|
||||||
std::size_t i = 0;
|
|
||||||
|
|
||||||
template <typename Type>
|
|
||||||
void check()
|
|
||||||
{
|
|
||||||
if constexpr (std::is_same_v<Type, std::nullptr_t>)
|
|
||||||
{
|
|
||||||
expect_null(component.children()[i]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
expect_non_null(component.children()[i]);
|
|
||||||
expect_dynamic_type(*component.children()[i], Type);
|
|
||||||
}
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename ... Types>
|
|
||||||
void check_children(impl::component & component)
|
|
||||||
{
|
|
||||||
expect_equal(component.children().size(), sizeof...(Types));
|
|
||||||
|
|
||||||
check_children_helper helper{component};
|
|
||||||
|
|
||||||
(void)helper;
|
|
||||||
(helper.check<Types>(), ...);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_factory_element)
|
|
||||||
{
|
|
||||||
test_component_factory factory;
|
|
||||||
|
|
||||||
auto test_ui = label{};
|
|
||||||
auto ui_root = factory.reconciliate(nullptr, test_ui);
|
|
||||||
|
|
||||||
expect_non_null(ui_root);
|
|
||||||
expect_dynamic_type(*ui_root, label_impl);
|
|
||||||
|
|
||||||
expect_equal(factory.label_count, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_factory_single__container)
|
|
||||||
{
|
|
||||||
test_component_factory factory;
|
|
||||||
|
|
||||||
auto test_ui = button{
|
|
||||||
.child = label{}
|
|
||||||
};
|
|
||||||
auto ui_root = factory.reconciliate(nullptr, test_ui);
|
|
||||||
|
|
||||||
expect_non_null(ui_root);
|
|
||||||
expect_dynamic_type(*ui_root, button_impl);
|
|
||||||
check_children<label_impl>(*ui_root);
|
|
||||||
|
|
||||||
expect_equal(factory.label_count, 1);
|
|
||||||
expect_equal(factory.button_count, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_factory_single__container__null)
|
|
||||||
{
|
|
||||||
test_component_factory factory;
|
|
||||||
|
|
||||||
auto test_ui = button{};
|
|
||||||
auto ui_root = factory.reconciliate(nullptr, test_ui);
|
|
||||||
|
|
||||||
expect_non_null(ui_root);
|
|
||||||
expect_dynamic_type(*ui_root, button_impl);
|
|
||||||
check_children<std::nullptr_t>(*ui_root);
|
|
||||||
|
|
||||||
expect_equal(factory.button_count, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_factory_container)
|
|
||||||
{
|
|
||||||
test_component_factory factory;
|
|
||||||
|
|
||||||
auto test_ui = stack_layout{{
|
|
||||||
{label{}},
|
|
||||||
{button{.child = label{}}},
|
|
||||||
{label{}},
|
|
||||||
}};
|
|
||||||
auto ui_root = factory.reconciliate(nullptr, test_ui);
|
|
||||||
|
|
||||||
expect_non_null(ui_root);
|
|
||||||
expect_dynamic_type(*ui_root, stack_layout_impl);
|
|
||||||
check_children<label_impl, button_impl, label_impl>(*ui_root);
|
|
||||||
check_children<label_impl>(*ui_root->children()[1]);
|
|
||||||
|
|
||||||
expect_equal(factory.label_count, 3);
|
|
||||||
expect_equal(factory.button_count, 1);
|
|
||||||
expect_equal(factory.stack_layout_count, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_factory_container__null)
|
|
||||||
{
|
|
||||||
test_component_factory factory;
|
|
||||||
|
|
||||||
auto test_ui = stack_layout{};
|
|
||||||
auto ui_root = factory.reconciliate(nullptr, test_ui);
|
|
||||||
|
|
||||||
expect_non_null(ui_root);
|
|
||||||
expect_dynamic_type(*ui_root, stack_layout_impl);
|
|
||||||
check_children<>(*ui_root);
|
|
||||||
|
|
||||||
expect_equal(factory.stack_layout_count, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_factory_container__null__child)
|
|
||||||
{
|
|
||||||
test_component_factory factory;
|
|
||||||
|
|
||||||
auto test_ui = stack_layout{{
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
}};
|
|
||||||
auto ui_root = factory.reconciliate(nullptr, test_ui);
|
|
||||||
|
|
||||||
expect_non_null(ui_root);
|
|
||||||
expect_dynamic_type(*ui_root, stack_layout_impl);
|
|
||||||
check_children<std::nullptr_t, std::nullptr_t>(*ui_root);
|
|
||||||
|
|
||||||
expect_equal(factory.stack_layout_count, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_reconciliate_single__container)
|
|
||||||
{
|
|
||||||
test_component_factory factory;
|
|
||||||
|
|
||||||
auto child = react::source<std::any>(label{});
|
|
||||||
|
|
||||||
expect_equal((*child).type(), typeid(label{}));
|
|
||||||
|
|
||||||
auto test_ui = button{
|
|
||||||
.child = child
|
|
||||||
};
|
|
||||||
|
|
||||||
expect_equal((*test_ui.child).type(), typeid(label{}));
|
|
||||||
|
|
||||||
auto ui_root = factory.reconciliate(nullptr, test_ui);
|
|
||||||
|
|
||||||
expect_non_null(ui_root);
|
|
||||||
expect_dynamic_type(*ui_root, button_impl);
|
|
||||||
check_children<label_impl>(*ui_root);
|
|
||||||
|
|
||||||
expect_equal(factory.rectangle_count, 0);
|
|
||||||
expect_equal(factory.label_count, 1);
|
|
||||||
expect_equal(factory.button_count, 1);
|
|
||||||
|
|
||||||
child.set(label{});
|
|
||||||
|
|
||||||
check_children<label_impl>(*ui_root);
|
|
||||||
|
|
||||||
expect_equal(factory.rectangle_count, 0);
|
|
||||||
expect_equal(factory.label_count, 1);
|
|
||||||
expect_equal(factory.button_count, 1);
|
|
||||||
|
|
||||||
child.set(rectangle{});
|
|
||||||
|
|
||||||
check_children<rectangle_impl>(*ui_root);
|
|
||||||
|
|
||||||
expect_equal(factory.rectangle_count, 1);
|
|
||||||
expect_equal(factory.label_count, 1);
|
|
||||||
expect_equal(factory.button_count, 1);
|
|
||||||
|
|
||||||
child.set(label{});
|
|
||||||
|
|
||||||
check_children<label_impl>(*ui_root);
|
|
||||||
|
|
||||||
expect_equal(factory.rectangle_count, 1);
|
|
||||||
expect_equal(factory.label_count, 2);
|
|
||||||
expect_equal(factory.button_count, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_reconciliate_container__no__keys)
|
|
||||||
{
|
|
||||||
test_component_factory factory;
|
|
||||||
|
|
||||||
react::source<std::vector<stack_layout::element>> children;
|
|
||||||
|
|
||||||
auto test_ui = stack_layout{children};
|
|
||||||
|
|
||||||
auto ui_root = factory.reconciliate(nullptr, test_ui);
|
|
||||||
|
|
||||||
expect_equal(factory.stack_layout_count, 1);
|
|
||||||
expect_equal(factory.label_count, 0);
|
|
||||||
expect_equal(factory.button_count, 0);
|
|
||||||
|
|
||||||
expect_non_null(ui_root);
|
|
||||||
expect_dynamic_type(*ui_root, stack_layout_impl);
|
|
||||||
check_children<>(*ui_root);
|
|
||||||
|
|
||||||
children.set({{label{}}});
|
|
||||||
|
|
||||||
check_children<label_impl>(*ui_root);
|
|
||||||
|
|
||||||
expect_equal(factory.stack_layout_count, 1);
|
|
||||||
expect_equal(factory.label_count, 1);
|
|
||||||
expect_equal(factory.button_count, 0);
|
|
||||||
|
|
||||||
children.set({{label{}}, {label{}}});
|
|
||||||
|
|
||||||
check_children<label_impl, label_impl>(*ui_root);
|
|
||||||
|
|
||||||
expect_equal(factory.stack_layout_count, 1);
|
|
||||||
expect_equal(factory.label_count, 2);
|
|
||||||
expect_equal(factory.button_count, 0);
|
|
||||||
|
|
||||||
children.set({{label{}}, {label{}}, {label{}}});
|
|
||||||
|
|
||||||
expect_equal(factory.stack_layout_count, 1);
|
|
||||||
expect_equal(factory.label_count, 3);
|
|
||||||
expect_equal(factory.button_count, 0);
|
|
||||||
|
|
||||||
children.set({{label{}}, {button{}}, {label{}}});
|
|
||||||
|
|
||||||
expect_equal(factory.stack_layout_count, 1);
|
|
||||||
expect_equal(factory.label_count, 3);
|
|
||||||
expect_equal(factory.button_count, 1);
|
|
||||||
|
|
||||||
children.set({{button{}}, {button{}}, {label{}}});
|
|
||||||
|
|
||||||
expect_equal(factory.stack_layout_count, 1);
|
|
||||||
expect_equal(factory.label_count, 3);
|
|
||||||
expect_equal(factory.button_count, 2);
|
|
||||||
|
|
||||||
children.set({{button{}}, {button{}}});
|
|
||||||
|
|
||||||
expect_equal(factory.stack_layout_count, 1);
|
|
||||||
expect_equal(factory.label_count, 3);
|
|
||||||
expect_equal(factory.button_count, 2);
|
|
||||||
|
|
||||||
children.set({{button{}}});
|
|
||||||
|
|
||||||
expect_equal(factory.stack_layout_count, 1);
|
|
||||||
expect_equal(factory.label_count, 3);
|
|
||||||
expect_equal(factory.button_count, 2);
|
|
||||||
|
|
||||||
children.set({});
|
|
||||||
|
|
||||||
expect_equal(factory.stack_layout_count, 1);
|
|
||||||
expect_equal(factory.label_count, 3);
|
|
||||||
expect_equal(factory.button_count, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_reconciliate_container__keys)
|
|
||||||
{
|
|
||||||
test_component_factory factory;
|
|
||||||
|
|
||||||
react::source<std::vector<stack_layout::element>> children;
|
|
||||||
|
|
||||||
auto test_ui = stack_layout{children};
|
|
||||||
|
|
||||||
auto ui_root = factory.reconciliate(nullptr, test_ui);
|
|
||||||
|
|
||||||
expect_equal(factory.stack_layout_count, 1);
|
|
||||||
expect_equal(factory.label_count, 0);
|
|
||||||
|
|
||||||
expect_non_null(ui_root);
|
|
||||||
expect_dynamic_type(*ui_root, stack_layout_impl);
|
|
||||||
check_children<>(*ui_root);
|
|
||||||
|
|
||||||
children.set({{label{}, "Label 0"}});
|
|
||||||
|
|
||||||
check_children<label_impl>(*ui_root);
|
|
||||||
|
|
||||||
auto label0 = ui_root->children()[0].get();
|
|
||||||
|
|
||||||
expect_equal(factory.stack_layout_count, 1);
|
|
||||||
expect_equal(factory.label_count, 1);
|
|
||||||
|
|
||||||
children.set({{label{}, "Label 0"}, {label{}, "Label 1"}});
|
|
||||||
|
|
||||||
check_children<label_impl, label_impl>(*ui_root);
|
|
||||||
expect_equal(ui_root->children()[0].get(), label0);
|
|
||||||
auto label1 = ui_root->children()[1].get();
|
|
||||||
|
|
||||||
expect_equal(factory.stack_layout_count, 1);
|
|
||||||
expect_equal(factory.label_count, 2);
|
|
||||||
|
|
||||||
children.set({{label{}, "Label 1"}, {label{}, "Label 0"}});
|
|
||||||
|
|
||||||
check_children<label_impl, label_impl>(*ui_root);
|
|
||||||
expect_equal(ui_root->children()[0].get(), label1);
|
|
||||||
expect_equal(ui_root->children()[1].get(), label0);
|
|
||||||
|
|
||||||
expect_equal(factory.stack_layout_count, 1);
|
|
||||||
expect_equal(factory.label_count, 2);
|
|
||||||
|
|
||||||
children.set({{label{}, "Label 0"}, {label{}, "Label 2"}});
|
|
||||||
|
|
||||||
check_children<label_impl, label_impl>(*ui_root);
|
|
||||||
expect_equal(ui_root->children()[0].get(), label0);
|
|
||||||
|
|
||||||
expect_equal(factory.stack_layout_count, 1);
|
|
||||||
expect_equal(factory.label_count, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,233 +0,0 @@
|
||||||
#include <psemek/test/test.hpp>
|
|
||||||
|
|
||||||
#include <psemek/ui/impl/size_polygon_utils.hpp>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
using namespace psemek;
|
|
||||||
using namespace psemek::ui::impl;
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
size_polygon normalize(size_polygon p)
|
|
||||||
{
|
|
||||||
if (p.empty())
|
|
||||||
return p;
|
|
||||||
auto it = std::min_element(p.begin(), p.end());
|
|
||||||
std::rotate(p.begin(), it, p.end());
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
void check_polygon_cut(size_polygon & polygon, int dimension, geom::interval<float> const & range, size_polygon const & result)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < polygon.size(); ++i)
|
|
||||||
{
|
|
||||||
expect_equal(normalize(cut(polygon, dimension, range)), normalize(result));
|
|
||||||
std::rotate(polygon.begin(), polygon.end() - 1, polygon.end());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void check_polygon_sum(size_polygon & polygon1, size_polygon & polygon2, size_polygon const & result_x, size_polygon const & result_y)
|
|
||||||
{
|
|
||||||
for (int i1 = 0; i1 < polygon1.size(); ++i1)
|
|
||||||
{
|
|
||||||
for (int i2 = 0; i2 < polygon2.size(); ++i2)
|
|
||||||
{
|
|
||||||
expect_equal(normalize(sum_along(polygon1, polygon2, 0)), normalize(result_x));
|
|
||||||
expect_equal(normalize(sum_along(polygon1, polygon2, 1)), normalize(result_y));
|
|
||||||
|
|
||||||
std::rotate(polygon2.begin(), polygon2.end() - 1, polygon2.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::rotate(polygon1.begin(), polygon1.end() - 1, polygon1.end());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_size__polygon_cut_empty)
|
|
||||||
{
|
|
||||||
expect_equal(cut(size_polygon{}, 0, {0.f, 10.f}), size_polygon{});
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_size__polygon_cut_point__x)
|
|
||||||
{
|
|
||||||
size_polygon polygon{{0.f, 0.f}};
|
|
||||||
|
|
||||||
check_polygon_cut(polygon, 0, {-5.f, -5.f}, size_polygon{});
|
|
||||||
check_polygon_cut(polygon, 0, {-5.f, 0.f}, polygon);
|
|
||||||
check_polygon_cut(polygon, 0, {-5.f, 5.f}, polygon);
|
|
||||||
check_polygon_cut(polygon, 0, {0.f, 0.f}, polygon);
|
|
||||||
check_polygon_cut(polygon, 0, {0.f, 5.f}, polygon);
|
|
||||||
check_polygon_cut(polygon, 0, {5.f, 5.f}, size_polygon{});
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_size__polygon_cut_point__y)
|
|
||||||
{
|
|
||||||
size_polygon polygon{{0.f, 0.f}};
|
|
||||||
|
|
||||||
check_polygon_cut(polygon, 1, {-5.f, -5.f}, size_polygon{});
|
|
||||||
check_polygon_cut(polygon, 1, {-5.f, 0.f}, polygon);
|
|
||||||
check_polygon_cut(polygon, 1, {-5.f, 5.f}, polygon);
|
|
||||||
check_polygon_cut(polygon, 1, {0.f, 0.f}, polygon);
|
|
||||||
check_polygon_cut(polygon, 1, {0.f, 5.f}, polygon);
|
|
||||||
check_polygon_cut(polygon, 1, {5.f, 5.f}, size_polygon{});
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_size__polygon_cut_segmentx__x__simple)
|
|
||||||
{
|
|
||||||
size_polygon polygon{{0.f, 0.f}, {10.f, 0.f}};
|
|
||||||
|
|
||||||
check_polygon_cut(polygon, 0, {-5.f, -5.f}, size_polygon{});
|
|
||||||
check_polygon_cut(polygon, 0, {-5.f, 5.f}, size_polygon{{0.f, 0.f}, {5.f, 0.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {-5.f, 15.f}, polygon);
|
|
||||||
check_polygon_cut(polygon, 0, {5.f, 15.f}, size_polygon{{5.f, 0.f}, {10.f, 0.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {15.f, 15.f}, size_polygon{});
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_size__polygon_cut_square__x__simple)
|
|
||||||
{
|
|
||||||
size_polygon polygon{{0.f, 0.f}, {10.f, 0.f}, {10.f, 10.f}, {0.f, 10.f}};
|
|
||||||
|
|
||||||
check_polygon_cut(polygon, 0, {-10.f, -2.f}, size_polygon{});
|
|
||||||
check_polygon_cut(polygon, 0, {-2.f, -2.f}, size_polygon{});
|
|
||||||
check_polygon_cut(polygon, 0, {-2.f, 2.f}, size_polygon{{0.f, 0.f}, {2.f, 0.f}, {2.f, 10.f}, {0.f, 10.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {2.f, 8.f}, size_polygon{{2.f, 0.f}, {8.f, 0.f}, {8.f, 10.f}, {2.f, 10.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {5.f, 5.f}, size_polygon{{5.f, 0.f}, {5.f, 10.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {8.f, 12.f}, size_polygon{{8.f, 0.f}, {10.f, 0.f}, {10.f, 10.f}, {8.f, 10.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {12.f, 20.f}, size_polygon{});
|
|
||||||
check_polygon_cut(polygon, 0, {12.f, 12.f}, size_polygon{});
|
|
||||||
check_polygon_cut(polygon, 0, {-2.f, 12.f}, polygon);
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_size__polygon_cut_square__x__degenerate)
|
|
||||||
{
|
|
||||||
size_polygon polygon{{0.f, 0.f}, {10.f, 0.f}, {10.f, 10.f}, {0.f, 10.f}};
|
|
||||||
|
|
||||||
check_polygon_cut(polygon, 0, {-10.f, 0.f}, size_polygon{{0.f, 0.f}, {0.f, 10.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {0.f, 0.f}, size_polygon{{0.f, 0.f}, {0.f, 10.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {-2.f, 10.f}, polygon);
|
|
||||||
check_polygon_cut(polygon, 0, {0.f, 10.f}, polygon);
|
|
||||||
check_polygon_cut(polygon, 0, {0.f, 12.f}, polygon);
|
|
||||||
check_polygon_cut(polygon, 0, {10.f, 10.f}, size_polygon{{10.f, 0.f}, {10.f, 10.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {10.f, 20.f}, size_polygon{{10.f, 0.f}, {10.f, 10.f}});
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_size__polygon_cut_square__y__simple)
|
|
||||||
{
|
|
||||||
size_polygon polygon{{0.f, 0.f}, {10.f, 0.f}, {10.f, 10.f}, {0.f, 10.f}};
|
|
||||||
|
|
||||||
check_polygon_cut(polygon, 1, {-10.f, -2.f}, size_polygon{});
|
|
||||||
check_polygon_cut(polygon, 1, {-2.f, -2.f}, size_polygon{});
|
|
||||||
check_polygon_cut(polygon, 1, {-2.f, 2.f}, size_polygon{{0.f, 0.f}, {10.f, 0.f}, {10.f, 2.f}, {0.f, 2.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {2.f, 8.f}, size_polygon{{0.f, 2.f}, {10.f, 2.f}, {10.f, 8.f}, {0.f, 8.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {5.f, 5.f}, size_polygon{{0.f, 5.f}, {10.f, 5.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {8.f, 12.f}, size_polygon{{0.f, 8.f}, {10.f, 8.f}, {10.f, 10.f}, {0.f, 10.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {12.f, 20.f}, size_polygon{});
|
|
||||||
check_polygon_cut(polygon, 1, {12.f, 12.f}, size_polygon{});
|
|
||||||
check_polygon_cut(polygon, 1, {-2.f, 12.f}, polygon);
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_size__polygon_cut_square__y__degenerate)
|
|
||||||
{
|
|
||||||
size_polygon polygon{{0.f, 0.f}, {10.f, 0.f}, {10.f, 10.f}, {0.f, 10.f}};
|
|
||||||
|
|
||||||
check_polygon_cut(polygon, 1, {-10.f, 0.f}, size_polygon{{0.f, 0.f}, {10.f, 0.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {0.f, 0.f}, size_polygon{{0.f, 0.f}, {10.f, 0.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {-2.f, 10.f}, polygon);
|
|
||||||
check_polygon_cut(polygon, 1, {0.f, 10.f}, polygon);
|
|
||||||
check_polygon_cut(polygon, 1, {0.f, 12.f}, polygon);
|
|
||||||
check_polygon_cut(polygon, 1, {10.f, 10.f}, size_polygon{{0.f, 10.f}, {10.f, 10.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {10.f, 20.f}, size_polygon{{0.f, 10.f}, {10.f, 10.f}});
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_size__polygon_cut_diamond__x__simple)
|
|
||||||
{
|
|
||||||
size_polygon polygon{{10.f, 0.f}, {20.f, 10.f}, {10.f, 20.f}, {0.f, 10.f}};
|
|
||||||
|
|
||||||
check_polygon_cut(polygon, 0, {-10.f, -5.f}, size_polygon{});
|
|
||||||
check_polygon_cut(polygon, 0, {-10.f, 5.f}, size_polygon{{0.f, 10.f}, {5.f, 5.f}, {5.f, 15.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {-10.f, 15.f}, size_polygon{{0.f, 10.f}, {10.f, 0.f}, {15.f, 5.f}, {15.f, 15.f}, {10.f, 20.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {5.f, 5.f}, size_polygon{{5.f, 5.f}, {5.f, 15.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {5.f, 15.f}, size_polygon{{5.f, 15.f}, {5.f, 5.f}, {10.f, 0.f}, {15.f, 5.f}, {15.f, 15.f}, {10.f, 20.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {5.f, 25.f}, size_polygon{{5.f, 15.f}, {5.f, 5.f}, {10.f, 0.f}, {20.f, 10.f}, {10.f, 20.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {15.f, 15.f}, size_polygon{{15.f, 5.f}, {15.f, 15.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {15.f, 25.f}, size_polygon{{15.f, 5.f}, {20.f, 10.f}, {15.f, 15.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {25.f, 30.f}, size_polygon{});
|
|
||||||
check_polygon_cut(polygon, 0, {-5.f, 25.f}, polygon);
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_size__polygon_cut_diamond__x__degenerate)
|
|
||||||
{
|
|
||||||
size_polygon polygon{{10.f, 0.f}, {20.f, 10.f}, {10.f, 20.f}, {0.f, 10.f}};
|
|
||||||
|
|
||||||
check_polygon_cut(polygon, 0, {-10.f, 0.f}, size_polygon{{0.f, 10.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {-10.f, 10.f}, size_polygon{{0.f, 10.f}, {10.f, 0.f}, {10.f, 20.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {-10.f, 20.f}, polygon);
|
|
||||||
check_polygon_cut(polygon, 0, {0.f, 0.f}, size_polygon{{0.f, 10.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {0.f, 5.f}, size_polygon{{0.f, 10.f}, {5.f, 5.f}, {5.f, 15.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {0.f, 10.f}, size_polygon{{0.f, 10.f}, {10.f, 0.f}, {10.f, 20.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {0.f, 15.f}, size_polygon{{0.f, 10.f}, {10.f, 0.f}, {15.f, 5.f}, {15.f, 15.f}, {10.f, 20.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {0.f, 20.f}, polygon);
|
|
||||||
check_polygon_cut(polygon, 0, {0.f, 25.f}, polygon);
|
|
||||||
check_polygon_cut(polygon, 0, {5.f, 10.f}, size_polygon{{5.f, 5.f}, {10.f, 0.f}, {10.f, 20.f}, {5.f, 15.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {5.f, 20.f}, size_polygon{{5.f, 5.f}, {10.f, 0.f}, {20.f, 10.f}, {10.f, 20.f}, {5.f, 15.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {10.f, 10.f}, size_polygon{{10.f, 0.f}, {10.f, 20.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {10.f, 15.f}, size_polygon{{10.f, 0.f}, {15.f, 5.f}, {15.f, 15.f}, {10.f, 20.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {10.f, 20.f}, size_polygon{{10.f, 0.f}, {20.f, 10.f}, {10.f, 20.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {10.f, 25.f}, size_polygon{{10.f, 0.f}, {20.f, 10.f}, {10.f, 20.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {15.f, 20.f}, size_polygon{{15.f, 5.f}, {20.f, 10.f}, {15.f, 15.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {20.f, 20.f}, size_polygon{{20.f, 10.f}});
|
|
||||||
check_polygon_cut(polygon, 0, {20.f, 25.f}, size_polygon{{20.f, 10.f}});
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_size__polygon_cut_diamond__y__simple)
|
|
||||||
{
|
|
||||||
size_polygon polygon{{10.f, 0.f}, {20.f, 10.f}, {10.f, 20.f}, {0.f, 10.f}};
|
|
||||||
|
|
||||||
check_polygon_cut(polygon, 1, {-10.f, -5.f}, size_polygon{});
|
|
||||||
check_polygon_cut(polygon, 1, {-10.f, 5.f}, size_polygon{{15.f, 5.f}, {5.f, 5.f}, {10.f, 0.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {-10.f, 15.f}, size_polygon{{20.f, 10.f}, {15.f, 15.f}, {5.f, 15.f}, {0.f, 10.f}, {10.f, 0.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {5.f, 5.f}, size_polygon{{15.f, 5.f}, {5.f, 5.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {5.f, 15.f}, size_polygon{{20.f, 10.f}, {15.f, 15.f}, {5.f, 15.f}, {0.f, 10.f}, {5.f, 5.f}, {15.f, 5.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {5.f, 25.f}, size_polygon{{20.f, 10.f}, {10.f, 20.f}, {0.f, 10.f}, {5.f, 5.f}, {15.f, 5.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {15.f, 15.f}, size_polygon{{15.f, 15.f}, {5.f, 15.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {15.f, 25.f}, size_polygon{{15.f, 15.f}, {10.f, 20.f}, {5.f, 15.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {25.f, 30.f}, size_polygon{});
|
|
||||||
check_polygon_cut(polygon, 1, {-5.f, 25.f}, polygon);
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_size__polygon_cut_diamond__y__degenerate)
|
|
||||||
{
|
|
||||||
size_polygon polygon{{10.f, 0.f}, {20.f, 10.f}, {10.f, 20.f}, {0.f, 10.f}};
|
|
||||||
|
|
||||||
check_polygon_cut(polygon, 1, {-10.f, 0.f}, size_polygon{{10.f, 0.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {-10.f, 10.f}, size_polygon{{20.f, 10.f}, {0.f, 10.f}, {10.f, 0.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {-10.f, 20.f}, polygon);
|
|
||||||
check_polygon_cut(polygon, 1, {0.f, 0.f}, size_polygon{{10.f, 0.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {0.f, 5.f}, size_polygon{{10.f, 0.f}, {15.f, 5.f}, {5.f, 5.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {0.f, 10.f}, size_polygon{{0.f, 10.f}, {10.f, 0.f}, {20.f, 10.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {0.f, 15.f}, size_polygon{{0.f, 10.f}, {10.f, 0.f}, {20.f, 10.f}, {15.f, 15.f}, {5.f, 15.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {0.f, 20.f}, polygon);
|
|
||||||
check_polygon_cut(polygon, 1, {0.f, 25.f}, polygon);
|
|
||||||
check_polygon_cut(polygon, 1, {5.f, 10.f}, size_polygon{{5.f, 5.f}, {15.f, 5.f}, {20.f, 10.f}, {0.f, 10.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {5.f, 20.f}, size_polygon{{5.f, 5.f}, {15.f, 5.f}, {20.f, 10.f}, {10.f, 20.f}, {0.f, 10.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {10.f, 10.f}, size_polygon{{0.f, 10.f}, {20.f, 10.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {10.f, 15.f}, size_polygon{{0.f, 10.f}, {20.f, 10.f}, {15.f, 15.f}, {5.f, 15.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {10.f, 20.f}, size_polygon{{0.f, 10.f}, {20.f, 10.f}, {10.f, 20.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {10.f, 25.f}, size_polygon{{0.f, 10.f}, {20.f, 10.f}, {10.f, 20.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {15.f, 20.f}, size_polygon{{5.f, 15.f}, {15.f, 15.f}, {10.f, 20.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {20.f, 20.f}, size_polygon{{10.f, 20.f}});
|
|
||||||
check_polygon_cut(polygon, 1, {20.f, 25.f}, size_polygon{{10.f, 20.f}});
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_size__polygon_sum_empty)
|
|
||||||
{
|
|
||||||
size_polygon polygon;
|
|
||||||
|
|
||||||
expect_equal(sum_along(polygon, polygon, 0), polygon);
|
|
||||||
expect_equal(sum_along(polygon, polygon, 1), polygon);
|
|
||||||
|
|
||||||
(void)check_polygon_sum;
|
|
||||||
}
|
|
||||||
|
|
@ -1,95 +0,0 @@
|
||||||
#include <psemek/test/test.hpp>
|
|
||||||
|
|
||||||
#include <psemek/ui/impl/size_polygon_utils.hpp>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
using namespace psemek;
|
|
||||||
using namespace psemek::ui::impl;
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
std::set<T> as_set(std::vector<T> const & v)
|
|
||||||
{
|
|
||||||
std::set<T> result;
|
|
||||||
for (auto const & x : v)
|
|
||||||
result.insert(x);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void check_polygon_min(size_polygon & polygon, size_polygon const & min_x, size_polygon const & min_y)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < polygon.size(); ++i)
|
|
||||||
{
|
|
||||||
expect_equal(as_set(min(polygon, 0)), as_set(min_x));
|
|
||||||
expect_equal(as_set(min(polygon, 1)), as_set(min_y));
|
|
||||||
|
|
||||||
std::rotate(polygon.begin(), polygon.end() - 1, polygon.end());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_size__polygon_min_empty)
|
|
||||||
{
|
|
||||||
size_polygon polygon;
|
|
||||||
|
|
||||||
expect_equal(min(polygon, 0), size_polygon{});
|
|
||||||
expect_equal(min(polygon, 1), size_polygon{});
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_size__polygon_min_edge__X)
|
|
||||||
{
|
|
||||||
size_polygon polygon{{0.f, 0.f}, {10.f, 0.f}};
|
|
||||||
size_polygon min_x{{0.f, 0.f}};
|
|
||||||
size_polygon min_y{{0.f, 0.f}, {10.f, 0.f}};
|
|
||||||
|
|
||||||
check_polygon_min(polygon, min_x, min_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_size__polygon_min_edge__Y)
|
|
||||||
{
|
|
||||||
size_polygon polygon{{0.f, 0.f}, {0.f, 10.f}};
|
|
||||||
size_polygon min_x{{0.f, 0.f}, {0.f, 10.f}};
|
|
||||||
size_polygon min_y{{0.f, 0.f}};
|
|
||||||
|
|
||||||
check_polygon_min(polygon, min_x, min_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_size__polygon_min_edge__XY)
|
|
||||||
{
|
|
||||||
size_polygon polygon{{0.f, 0.f}, {10.f, 10.f}};
|
|
||||||
size_polygon min_x{{0.f, 0.f}};
|
|
||||||
size_polygon min_y{{0.f, 0.f}};
|
|
||||||
|
|
||||||
check_polygon_min(polygon, min_x, min_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_size__polygon_min_square)
|
|
||||||
{
|
|
||||||
size_polygon polygon{{0.f, 0.f}, {10.f, 0.f}, {10.f, 10.f}, {0.f, 10.f}};
|
|
||||||
size_polygon min_x{{0.f, 0.f}, {0.f, 10.f}};
|
|
||||||
size_polygon min_y{{0.f, 0.f}, {10.f, 0.f}};
|
|
||||||
|
|
||||||
check_polygon_min(polygon, min_x, min_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_size__polygon_min_diamond)
|
|
||||||
{
|
|
||||||
size_polygon polygon{{0.f, 10.f}, {10.f, 0.f}, {20.f, 10.f}, {10.f, 20.f}};
|
|
||||||
size_polygon min_x{{0.f, 10.f}};
|
|
||||||
size_polygon min_y{{10.f, 0.f}};
|
|
||||||
|
|
||||||
check_polygon_min(polygon, min_x, min_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case(ui_impl_size__polygon_min_octahedron)
|
|
||||||
{
|
|
||||||
size_polygon polygon{{10.f, 0.f}, {20.f, 0.f}, {30.f, 10.f}, {30.f, 20.f}, {20.f, 30.f}, {10.f, 30.f}, {0.f, 20.f}, {0.f, 10.f}};
|
|
||||||
size_polygon min_x{{0.f, 10.f}, {0.f, 20.f}};
|
|
||||||
size_polygon min_y{{10.f, 0.f}, {20.f, 0.f}};
|
|
||||||
|
|
||||||
check_polygon_min(polygon, min_x, min_y);
|
|
||||||
}
|
|
||||||
Loading…
Add table
Reference in a new issue