New UI library wip
This commit is contained in:
parent
5895c01c4c
commit
933734bd2e
23 changed files with 840 additions and 293 deletions
|
|
@ -48,10 +48,11 @@ namespace psemek::react
|
||||||
return value<R>(detail::internal_tag{}, std::move(node));
|
return value<R>(detail::internal_tag{}, std::move(node));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F, typename T>
|
template <typename F, typename T, typename G>
|
||||||
auto map_impl(F func, std::vector<value<T>> args)
|
auto map_vector_impl(F func, std::vector<value<T>> args, G transform)
|
||||||
{
|
{
|
||||||
using R = std::decay_t<decltype(func(std::declval<std::vector<T>>()))>;
|
using H = std::decay_t<decltype(transform(std::declval<T>()))>;
|
||||||
|
using R = std::decay_t<decltype(func(std::declval<std::vector<H>>()))>;
|
||||||
|
|
||||||
auto node = std::make_shared<detail::node<R>>();
|
auto node = std::make_shared<detail::node<R>>();
|
||||||
auto weak_node = std::weak_ptr{node};
|
auto weak_node = std::weak_ptr{node};
|
||||||
|
|
@ -88,11 +89,14 @@ namespace psemek::react
|
||||||
func = std::move(func),
|
func = std::move(func),
|
||||||
internal_tokens = std::move(internal_tokens),
|
internal_tokens = std::move(internal_tokens),
|
||||||
external_tokens = std::move(external_tokens),
|
external_tokens = std::move(external_tokens),
|
||||||
args = std::move(args)]() -> R {
|
args = std::move(args),
|
||||||
std::vector<T> arg_values;
|
transform = std::move(transform)]() -> R {
|
||||||
|
std::vector<H> arg_values;
|
||||||
arg_values.reserve(args.size());
|
arg_values.reserve(args.size());
|
||||||
for (auto const & arg : args)
|
for (auto const & arg : args)
|
||||||
arg_values.push_back(*arg);
|
{
|
||||||
|
arg_values.push_back(transform(arg));
|
||||||
|
}
|
||||||
return func(std::move(arg_values));
|
return func(std::move(arg_values));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -108,15 +112,34 @@ namespace psemek::react
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F, typename T>
|
template <typename F, typename T>
|
||||||
auto map(F func, std::vector<value<T>> args)
|
auto map_vector(F func, std::vector<value<T>> args)
|
||||||
{
|
{
|
||||||
return detail::map_impl(std::move(func), std::move(args));
|
return detail::map_vector_impl(std::move(func), std::move(args), [](T const & value){ return value; });
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename F, typename T, typename G>
|
||||||
value<std::vector<T>> unpack(std::vector<value<T>> args)
|
auto map_vector(F func, std::vector<value<T>> args, G transform)
|
||||||
{
|
{
|
||||||
return map([](std::vector<T> x) -> std::vector<T> { return x; }, std::move(args));
|
return detail::map_vector_impl(std::move(func), std::move(args), transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr auto unpack = []<typename T>(std::vector<value<T>> args)
|
||||||
|
{
|
||||||
|
return map_vector([](std::vector<T> x) -> std::vector<T> { return x; }, std::move(args), [](value<T> const & arg){ return arg ? *arg : T{}; });
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr auto unpack_with_default = []<typename T>(T const & default_value)
|
||||||
|
{
|
||||||
|
return [default_value](std::vector<value<T>> args){
|
||||||
|
return map_vector([](std::vector<T> x) -> std::vector<T> { return x; }, std::move(args), [default_value](value<T> const & arg){ return arg ? *arg : default_value; });
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr auto unpack_with_transform = []<typename G>(G transform)
|
||||||
|
{
|
||||||
|
return [transform = std::move(transform)]<typename T>(std::vector<value<T>> args){
|
||||||
|
return map_vector([]<typename H>(std::vector<H> x) -> std::vector<H> { return x; }, std::move(args), transform);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,17 +15,12 @@ namespace psemek::ui
|
||||||
struct minimized
|
struct minimized
|
||||||
{};
|
{};
|
||||||
|
|
||||||
struct fixed
|
|
||||||
{
|
|
||||||
float value = 0.f;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct weight
|
struct weight
|
||||||
{
|
{
|
||||||
float value = 1.f;
|
float value = 1.f;
|
||||||
};
|
};
|
||||||
|
|
||||||
using size_policy = std::variant<minimized, fixed, weight>;
|
using size_policy = std::variant<minimized, weight>;
|
||||||
|
|
||||||
struct element
|
struct element
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include <psemek/ui/impl/container.hpp>
|
#include <psemek/ui/impl/container.hpp>
|
||||||
#include <psemek/ui/box_layout.hpp>
|
#include <psemek/ui/box_layout.hpp>
|
||||||
#include <psemek/react/value.hpp>
|
#include <psemek/react/value.hpp>
|
||||||
|
#include <psemek/react/source.hpp>
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
namespace psemek::ui::impl
|
||||||
{
|
{
|
||||||
|
|
@ -36,13 +37,18 @@ namespace psemek::ui::impl
|
||||||
box_layout_base(react::value<float> margin);
|
box_layout_base(react::value<float> margin);
|
||||||
|
|
||||||
void reshape(geom::box<float, 2> const & new_shape) override;
|
void reshape(geom::box<float, 2> const & new_shape) override;
|
||||||
geom::interval<float> size_constraints(int dimension, float other_dimension_size) const override;
|
react::value<size_polygon> 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);
|
void update(typename detail::box_layout_type<Dimension>::type const & box_layout);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
react::value<float> margin_;
|
react::value<float> margin_;
|
||||||
react::value<std::vector<react::value<box_layout::size_policy>>> size_policies_;
|
react::value<std::vector<react::value<box_layout::size_policy>>> size_policies_;
|
||||||
|
react::source<std::vector<react::value<size_polygon>>> children_size_constraints_;
|
||||||
|
react::value<size_polygon> size_constraints_;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern template struct box_layout_base<0>;
|
extern template struct box_layout_base<0>;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <psemek/util/span.hpp>
|
#include <psemek/ui/impl/size_polygon.hpp>
|
||||||
|
#include <psemek/react/value.hpp>
|
||||||
#include <psemek/geom/box.hpp>
|
#include <psemek/geom/box.hpp>
|
||||||
#include <psemek/geom/interval.hpp>
|
#include <psemek/geom/interval.hpp>
|
||||||
|
#include <psemek/util/span.hpp>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
|
@ -13,14 +15,15 @@ namespace psemek::ui::impl
|
||||||
{
|
{
|
||||||
static constexpr float infinity = std::numeric_limits<float>::infinity();
|
static constexpr float infinity = std::numeric_limits<float>::infinity();
|
||||||
|
|
||||||
|
static size_polygon const & default_size_polygon();
|
||||||
|
static react::value<size_polygon> default_size_constraints();
|
||||||
|
|
||||||
virtual util::span<std::unique_ptr<component> const> children() const;
|
virtual util::span<std::unique_ptr<component> const> children() const;
|
||||||
|
|
||||||
virtual geom::box<float, 2> const & shape() const;
|
virtual geom::box<float, 2> const & shape() const;
|
||||||
virtual void reshape(geom::box<float, 2> const & new_shape);
|
virtual void reshape(geom::box<float, 2> const & new_shape);
|
||||||
|
|
||||||
// size_constraints(0, height) is width_constraints(height)
|
virtual react::value<size_polygon> size_constraints() const;
|
||||||
// size_constraints(1, width) is height_constraints(width)
|
|
||||||
virtual geom::interval<float> size_constraints(int dimension, float other_dimension_size) const;
|
|
||||||
|
|
||||||
virtual ~component() {}
|
virtual ~component() {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,50 +14,17 @@ namespace psemek::ui::impl
|
||||||
struct container
|
struct container
|
||||||
: component
|
: component
|
||||||
{
|
{
|
||||||
void set_children(std::vector<std::unique_ptr<component>> children)
|
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);
|
||||||
children_ = std::move(children);
|
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);
|
||||||
|
|
||||||
void set_children_token(util::signal<std::vector<std::any>>::subscription_token token)
|
virtual std::vector<std::unique_ptr<component>> release_children();
|
||||||
{
|
virtual util::signal<void>::subscription_token release_children_token();
|
||||||
children_token_ = std::move(token);
|
virtual std::vector<util::signal<void>::subscription_token> release_child_tokens();
|
||||||
}
|
virtual std::vector<std::optional<key>> release_child_keys();
|
||||||
|
|
||||||
void set_child_tokens(std::vector<util::signal<std::any>::subscription_token> tokens)
|
util::span<std::unique_ptr<component> const> children() const override;
|
||||||
{
|
|
||||||
child_tokens_ = std::move(tokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_child_keys(std::vector<std::optional<key>> keys)
|
|
||||||
{
|
|
||||||
child_keys_ = std::move(keys);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<component>> release_children()
|
|
||||||
{
|
|
||||||
return std::move(children_);
|
|
||||||
}
|
|
||||||
|
|
||||||
util::signal<void>::subscription_token release_children_token()
|
|
||||||
{
|
|
||||||
return std::move(children_token_);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<util::signal<void>::subscription_token> release_child_tokens()
|
|
||||||
{
|
|
||||||
return std::move(child_tokens_);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::optional<key>> release_child_keys()
|
|
||||||
{
|
|
||||||
return std::move(child_keys_);
|
|
||||||
}
|
|
||||||
|
|
||||||
util::span<std::unique_ptr<component> const> children() const override
|
|
||||||
{
|
|
||||||
return children_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
util::signal<void>::subscription_token children_token_;
|
util::signal<void>::subscription_token children_token_;
|
||||||
|
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <psemek/ui/impl/component.hpp>
|
|
||||||
#include <psemek/ui/rectangle.hpp>
|
|
||||||
#include <psemek/react/value.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
struct rectangle_base
|
|
||||||
: component
|
|
||||||
{
|
|
||||||
geom::interval<float> size_constraints(int dimension, float other_dimension_size) const override;
|
|
||||||
|
|
||||||
void update(rectangle const & value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
react::value<bool> square_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -12,35 +12,13 @@ namespace psemek::ui::impl
|
||||||
struct single_container
|
struct single_container
|
||||||
: component
|
: component
|
||||||
{
|
{
|
||||||
void set_child(std::unique_ptr<component> child)
|
util::span<std::unique_ptr<component> const> children() const override;
|
||||||
{
|
|
||||||
child_ = std::move(child);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_child_token(util::signal<void>::subscription_token token)
|
virtual void set_child(std::unique_ptr<component> child);
|
||||||
{
|
virtual void set_child_token(util::signal<void>::subscription_token token);
|
||||||
child_token_ = std::move(token);
|
virtual component * child() const;
|
||||||
}
|
virtual std::unique_ptr<component> release_child();
|
||||||
|
virtual util::signal<void>::subscription_token release_child_token();
|
||||||
component * child() const
|
|
||||||
{
|
|
||||||
return child_.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<component> release_child()
|
|
||||||
{
|
|
||||||
return std::move(child_);
|
|
||||||
}
|
|
||||||
|
|
||||||
util::signal<void>::subscription_token release_child_token()
|
|
||||||
{
|
|
||||||
return std::move(child_token_);
|
|
||||||
}
|
|
||||||
|
|
||||||
util::span<std::unique_ptr<component> const> children() const override
|
|
||||||
{
|
|
||||||
return {&child_, 1};
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
util::signal<void>::subscription_token child_token_;
|
util::signal<void>::subscription_token child_token_;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <psemek/ui/impl/single_container.hpp>
|
#include <psemek/ui/impl/single_container.hpp>
|
||||||
#include <psemek/react/value.hpp>
|
#include <psemek/react/value.hpp>
|
||||||
|
#include <psemek/react/source.hpp>
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
namespace psemek::ui::impl
|
||||||
{
|
{
|
||||||
|
|
@ -12,10 +13,15 @@ namespace psemek::ui::impl
|
||||||
single_container_base(react::value<geom::vector<float, 2>> margin);
|
single_container_base(react::value<geom::vector<float, 2>> margin);
|
||||||
|
|
||||||
void reshape(geom::box<float, 2> const & new_shape) override;
|
void reshape(geom::box<float, 2> const & new_shape) override;
|
||||||
geom::interval<float> size_constraints(int dimension, float other_dimension_size) const override;
|
react::value<size_polygon> size_constraints() const override;
|
||||||
|
|
||||||
|
void set_child(std::unique_ptr<component> child) override;
|
||||||
|
std::unique_ptr<component> release_child() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
react::value<geom::vector<float, 2>> margin_;
|
react::value<geom::vector<float, 2>> margin_;
|
||||||
|
react::source<react::value<size_polygon>> child_size_constraints_;
|
||||||
|
react::value<size_polygon> size_constraints_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
15
libs/ui/include/psemek/ui/impl/size_polygon.hpp
Normal file
15
libs/ui/include/psemek/ui/impl/size_polygon.hpp
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <psemek/geom/point.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace psemek::ui::impl
|
||||||
|
{
|
||||||
|
|
||||||
|
constexpr float max_size = 16384.f;
|
||||||
|
|
||||||
|
// Convex, in CCW order
|
||||||
|
using size_polygon = std::vector<geom::point<float, 2>>;
|
||||||
|
|
||||||
|
}
|
||||||
19
libs/ui/include/psemek/ui/impl/size_polygon_utils.hpp
Normal file
19
libs/ui/include/psemek/ui/impl/size_polygon_utils.hpp
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <psemek/ui/impl/size_polygon.hpp>
|
||||||
|
#include <psemek/geom/interval.hpp>
|
||||||
|
|
||||||
|
namespace psemek::ui::impl
|
||||||
|
{
|
||||||
|
|
||||||
|
size_polygon shift(size_polygon polygon, geom::vector<float, 2> const & delta);
|
||||||
|
|
||||||
|
size_polygon min(size_polygon const & polygon, int dimension);
|
||||||
|
|
||||||
|
size_polygon intersect(size_polygon const & polygon1, size_polygon const & polygon2);
|
||||||
|
|
||||||
|
geom::interval<float> range(size_polygon const & polygon, int dimension);
|
||||||
|
size_polygon cut(size_polygon const & polygon, int dimension, geom::interval<float> const & range);
|
||||||
|
size_polygon sum_along(size_polygon const & polygon1, size_polygon const & polygon2, int dimension);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include <psemek/ui/impl/container.hpp>
|
#include <psemek/ui/impl/container.hpp>
|
||||||
#include <psemek/ui/stack_layout.hpp>
|
#include <psemek/ui/stack_layout.hpp>
|
||||||
#include <psemek/react/value.hpp>
|
#include <psemek/react/value.hpp>
|
||||||
|
#include <psemek/react/source.hpp>
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
namespace psemek::ui::impl
|
||||||
{
|
{
|
||||||
|
|
@ -10,11 +11,20 @@ namespace psemek::ui::impl
|
||||||
struct stack_layout_base
|
struct stack_layout_base
|
||||||
: container
|
: container
|
||||||
{
|
{
|
||||||
|
stack_layout_base();
|
||||||
|
|
||||||
void reshape(geom::box<float, 2> const & new_shape) override;
|
void reshape(geom::box<float, 2> const & new_shape) override;
|
||||||
geom::interval<float> size_constraints(int dimension, float other_dimension_size) const override;
|
react::value<size_polygon> 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 &)
|
void update(stack_layout const &)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
private:
|
||||||
|
react::source<std::vector<react::value<size_polygon>>> children_size_constraints_;
|
||||||
|
react::value<size_polygon> size_constraints_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ namespace psemek::ui
|
||||||
struct rectangle
|
struct rectangle
|
||||||
{
|
{
|
||||||
react::value<gfx::color_rgba> color = {};
|
react::value<gfx::color_rgba> color = {};
|
||||||
react::value<bool> square = {};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include <psemek/ui/impl/box_layout_base.hpp>
|
#include <psemek/ui/impl/box_layout_base.hpp>
|
||||||
|
#include <psemek/ui/impl/size_polygon_utils.hpp>
|
||||||
#include <psemek/react/map.hpp>
|
#include <psemek/react/map.hpp>
|
||||||
#include <psemek/react/join.hpp>
|
#include <psemek/react/join.hpp>
|
||||||
|
|
||||||
|
|
@ -11,14 +12,55 @@ namespace psemek::ui::impl
|
||||||
static constexpr box_layout::size_policy default_policy = box_layout::weight{};
|
static constexpr box_layout::size_policy default_policy = box_layout::weight{};
|
||||||
|
|
||||||
template <int Dimension>
|
template <int Dimension>
|
||||||
std::vector<float> allocate(float total_size, float other_dimension_size,
|
size_polygon compute_size_constraints(std::vector<box_layout::size_policy> const & size_policies,
|
||||||
std::vector<react::value<box_layout::size_policy>> const & policies, util::span<std::unique_ptr<component> const> children)
|
std::vector<size_polygon const *> const & children_size_constraints, float margin)
|
||||||
|
{
|
||||||
|
size_polygon minimized{{0.f, 0.f}, {0.f, max_size}};
|
||||||
|
size_polygon weight_unit = minimized;
|
||||||
|
float weight_sum = 0.f;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < size_policies.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]))
|
||||||
|
{
|
||||||
|
size_polygon child = *children_size_constraints[i];
|
||||||
|
for (auto & p : child)
|
||||||
|
p[Dimension] /= weight->value;
|
||||||
|
weight_unit = sum_along(weight_unit, child, Dimension);
|
||||||
|
weight_sum += weight->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = std::move(minimized);
|
||||||
|
|
||||||
|
if (weight_sum > 0.f)
|
||||||
|
{
|
||||||
|
for (auto & p : weight_unit)
|
||||||
|
p[Dimension] *= weight_sum;
|
||||||
|
result = sum_along(result, weight_unit, Dimension);
|
||||||
|
}
|
||||||
|
|
||||||
|
geom::vector shift_delta{0.f, 0.f};
|
||||||
|
shift_delta[Dimension] = margin;
|
||||||
|
|
||||||
|
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 & policies,
|
||||||
|
util::span<std::unique_ptr<component> const> children)
|
||||||
{
|
{
|
||||||
std::vector<float> result(policies.size(), 0.f);
|
std::vector<float> result(policies.size(), 0.f);
|
||||||
|
|
||||||
float total_weight = 0.f;
|
float total_weight = 0.f;
|
||||||
|
|
||||||
// First, allocate minimized & fixed elements
|
// First, allocate minimized elements
|
||||||
// Also compute the total weight for weighted elements
|
// Also compute the total weight for weighted elements
|
||||||
for (std::size_t i = 0; i < policies.size(); ++i)
|
for (std::size_t i = 0; i < policies.size(); ++i)
|
||||||
{
|
{
|
||||||
|
|
@ -26,12 +68,7 @@ namespace psemek::ui::impl
|
||||||
|
|
||||||
if (std::get_if<box_layout::minimized>(&policy))
|
if (std::get_if<box_layout::minimized>(&policy))
|
||||||
{
|
{
|
||||||
result[i] = children[i]->size_constraints(Dimension, other_dimension_size).min;
|
result[i] = min(*children[i]->size_constraints(), Dimension).front()[Dimension];
|
||||||
total_size -= result[i];
|
|
||||||
}
|
|
||||||
else if (auto fixed = std::get_if<box_layout::fixed>(&policy))
|
|
||||||
{
|
|
||||||
result[i] = fixed->value;
|
|
||||||
total_size -= result[i];
|
total_size -= result[i];
|
||||||
}
|
}
|
||||||
else if (auto weight = std::get_if<box_layout::weight>(&policy))
|
else if (auto weight = std::get_if<box_layout::weight>(&policy))
|
||||||
|
|
@ -59,6 +96,14 @@ namespace psemek::ui::impl
|
||||||
template <int Dimension>
|
template <int Dimension>
|
||||||
box_layout_base<Dimension>::box_layout_base(react::value<float> margin)
|
box_layout_base<Dimension>::box_layout_base(react::value<float> margin)
|
||||||
: margin_(margin)
|
: margin_(margin)
|
||||||
|
, size_policies_({})
|
||||||
|
, children_size_constraints_()
|
||||||
|
, size_constraints_(react::map([](auto const & size_policies, auto const & children_size_constraints, auto const & margin){
|
||||||
|
return compute_size_constraints<Dimension>(size_policies, children_size_constraints, margin);
|
||||||
|
},
|
||||||
|
react::join(react::map(react::unpack_with_default(default_policy), size_policies_)),
|
||||||
|
react::join(react::map(react::unpack_with_transform([](react::value<size_polygon> const & arg){ return &(*arg); }), children_size_constraints_)),
|
||||||
|
margin_))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
template <int Dimension>
|
template <int Dimension>
|
||||||
|
|
@ -71,7 +116,7 @@ namespace psemek::ui::impl
|
||||||
|
|
||||||
float const margin = *margin_;
|
float const margin = *margin_;
|
||||||
|
|
||||||
auto sizes = allocate<Dimension>(new_shape[Dimension].length() - (children().size() - 1) * margin, new_shape[Dimension ^ 1].length(), *size_policies_, children());
|
auto sizes = allocate<Dimension>(new_shape[Dimension].length() - (children().size() - 1) * margin, *size_policies_, children());
|
||||||
|
|
||||||
float pen = new_shape[Dimension].min;
|
float pen = new_shape[Dimension].min;
|
||||||
for (std::size_t i = 0; i < children().size(); ++i)
|
for (std::size_t i = 0; i < children().size(); ++i)
|
||||||
|
|
@ -89,83 +134,33 @@ namespace psemek::ui::impl
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int Dimension>
|
template <int Dimension>
|
||||||
geom::interval<float> box_layout_base<Dimension>::size_constraints(int dimension, float other_dimension_size) const
|
react::value<size_polygon> box_layout_base<Dimension>::size_constraints() const
|
||||||
{
|
{
|
||||||
if (children().empty())
|
return size_constraints_;
|
||||||
return container::size_constraints(dimension, other_dimension_size);
|
}
|
||||||
|
|
||||||
float const margin = *margin_;
|
template <int Dimension>
|
||||||
|
void box_layout_base<Dimension>::set_children(std::vector<std::unique_ptr<component>> children)
|
||||||
|
{
|
||||||
|
container::set_children(std::move(children));
|
||||||
|
|
||||||
if (dimension == Dimension)
|
std::vector<react::value<size_polygon>> children_size_constraints;
|
||||||
|
for (auto const & child : this->children())
|
||||||
{
|
{
|
||||||
auto result = geom::interval{0.f, 0.f};
|
if (child)
|
||||||
|
children_size_constraints.push_back(child->size_constraints());
|
||||||
auto const & policies = *size_policies_;
|
else
|
||||||
|
children_size_constraints.push_back(default_size_constraints());
|
||||||
auto weighted_unit_constraints = geom::interval{0.f, infinity};
|
|
||||||
float weight_sum = 0.f;
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < children().size(); ++i)
|
|
||||||
{
|
|
||||||
auto const & child = children()[i];
|
|
||||||
auto const policy = policies[i] ? *policies[i] : default_policy;
|
|
||||||
|
|
||||||
if (auto fixed = std::get_if<box_layout::fixed>(&policy))
|
|
||||||
result += fixed->value;
|
|
||||||
else if (std::get_if<box_layout::minimized>(&policy))
|
|
||||||
{
|
|
||||||
if (child)
|
|
||||||
{
|
|
||||||
auto child_constraints = child->size_constraints(dimension, other_dimension_size);
|
|
||||||
result.min += child_constraints.min;
|
|
||||||
result.max += child_constraints.max;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result.max = infinity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (auto weight = std::get_if<box_layout::weight>(&policy))
|
|
||||||
{
|
|
||||||
if (child)
|
|
||||||
{
|
|
||||||
auto child_constraints = child->size_constraints(dimension, other_dimension_size);
|
|
||||||
child_constraints.min /= weight->value;
|
|
||||||
child_constraints.max /= weight->value;
|
|
||||||
weighted_unit_constraints &= child_constraints;
|
|
||||||
}
|
|
||||||
|
|
||||||
weight_sum += weight->value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent NaN in result.max (inf * 0)
|
|
||||||
if (weight_sum > 0.f)
|
|
||||||
{
|
|
||||||
result.min += weighted_unit_constraints.min * weight_sum;
|
|
||||||
result.max += weighted_unit_constraints.max * weight_sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
result += (children().size() - 1) * margin;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
else
|
children_size_constraints_.set(std::move(children_size_constraints));
|
||||||
{
|
}
|
||||||
float const margin = *margin_;
|
|
||||||
|
|
||||||
auto sizes = allocate<Dimension>(other_dimension_size - (children().size() - 1) * margin, shape()[1].length(), *size_policies_, children());
|
template <int Dimension>
|
||||||
|
std::vector<std::unique_ptr<component>> box_layout_base<Dimension>::release_children()
|
||||||
auto result = container::size_constraints(dimension, other_dimension_size);
|
{
|
||||||
|
auto result = container::release_children();
|
||||||
for (std::size_t i = 0; i < children().size(); ++i)
|
children_size_constraints_.set({});
|
||||||
{
|
return result;
|
||||||
if (!children()[i]) continue;
|
|
||||||
result &= children()[i]->size_constraints(dimension, sizes[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int Dimension>
|
template <int Dimension>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,24 @@
|
||||||
namespace psemek::ui::impl
|
namespace psemek::ui::impl
|
||||||
{
|
{
|
||||||
|
|
||||||
|
size_polygon const & component::default_size_polygon()
|
||||||
|
{
|
||||||
|
static size_polygon const result{{
|
||||||
|
{max_size, 0.f},
|
||||||
|
{0.f, 0.f},
|
||||||
|
{0.f, max_size},
|
||||||
|
{max_size, max_size}
|
||||||
|
}};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
react::value<size_polygon> component::default_size_constraints()
|
||||||
|
{
|
||||||
|
static react::value<size_polygon> const result{default_size_polygon()};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
util::span<std::unique_ptr<component> const> component::children() const
|
util::span<std::unique_ptr<component> const> component::children() const
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
|
|
@ -18,9 +36,9 @@ namespace psemek::ui::impl
|
||||||
shape_ = new_shape;
|
shape_ = new_shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
geom::interval<float> component::size_constraints(int, float) const
|
react::value<size_polygon> component::size_constraints() const
|
||||||
{
|
{
|
||||||
return {0.f, infinity};
|
return default_size_constraints();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
51
libs/ui/source/impl/container.cpp
Normal file
51
libs/ui/source/impl/container.cpp
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
#include <psemek/ui/impl/container.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<void>::subscription_token container::release_children_token()
|
||||||
|
{
|
||||||
|
return std::move(children_token_);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<util::signal<void>::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,19 +0,0 @@
|
||||||
#include <psemek/ui/impl/rectangle_base.hpp>
|
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
|
||||||
{
|
|
||||||
|
|
||||||
geom::interval<float> rectangle_base::size_constraints(int dimension, float other_dimension_size) const
|
|
||||||
{
|
|
||||||
if (square_ && *square_)
|
|
||||||
return {other_dimension_size, other_dimension_size};
|
|
||||||
else
|
|
||||||
return component::size_constraints(dimension, other_dimension_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void rectangle_base::update(rectangle const & value)
|
|
||||||
{
|
|
||||||
square_ = value.square;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
36
libs/ui/source/impl/single_container.cpp
Normal file
36
libs/ui/source/impl/single_container.cpp
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
#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<void>::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<void>::subscription_token single_container::release_child_token()
|
||||||
|
{
|
||||||
|
return std::move(child_token_);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,17 @@
|
||||||
#include <psemek/ui/impl/single_container_base.hpp>
|
#include <psemek/ui/impl/single_container_base.hpp>
|
||||||
|
#include <psemek/ui/impl/size_polygon_utils.hpp>
|
||||||
|
#include <psemek/react/map.hpp>
|
||||||
|
#include <psemek/react/join.hpp>
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
namespace psemek::ui::impl
|
||||||
{
|
{
|
||||||
|
|
||||||
single_container_base::single_container_base(react::value<geom::vector<float, 2>> margin)
|
single_container_base::single_container_base(react::value<geom::vector<float, 2>> margin)
|
||||||
: margin_(std::move(margin))
|
: margin_(margin)
|
||||||
|
, child_size_constraints_(default_size_constraints())
|
||||||
|
, size_constraints_(react::map([](size_polygon 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)
|
void single_container_base::reshape(geom::box<float, 2> const & new_shape)
|
||||||
|
|
@ -15,14 +22,23 @@ namespace psemek::ui::impl
|
||||||
child()->reshape(geom::shrink(new_shape, *margin_));
|
child()->reshape(geom::shrink(new_shape, *margin_));
|
||||||
}
|
}
|
||||||
|
|
||||||
geom::interval<float> single_container_base::size_constraints(int dimension, float other_dimension_size) const
|
react::value<size_polygon> single_container_base::size_constraints() const
|
||||||
{
|
{
|
||||||
geom::interval<float> result = single_container::size_constraints(dimension, other_dimension_size);
|
return size_constraints_;
|
||||||
|
|
||||||
if (child())
|
|
||||||
result = child()->size_constraints(dimension, other_dimension_size - 2.f * (*margin_)[dimension ^ 1]);
|
|
||||||
|
|
||||||
return result + 2.f * (*margin_)[dimension];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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() : default_size_constraints());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<component> single_container_base::release_child()
|
||||||
|
{
|
||||||
|
auto child = single_container::release_child();
|
||||||
|
child_size_constraints_.set(default_size_constraints());
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
135
libs/ui/source/impl/size_polygon_utils.cpp
Normal file
135
libs/ui/source/impl/size_polygon_utils.cpp
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
#include <psemek/ui/impl/size_polygon_utils.hpp>
|
||||||
|
#include <psemek/geom/math.hpp>
|
||||||
|
#include <psemek/util/not_implemented.hpp>
|
||||||
|
#include <psemek/util/cyclic_iterator.hpp>
|
||||||
|
|
||||||
|
namespace psemek::ui::impl
|
||||||
|
{
|
||||||
|
|
||||||
|
size_polygon shift(size_polygon polygon, geom::vector<float, 2> const & delta)
|
||||||
|
{
|
||||||
|
for (auto & p : polygon)
|
||||||
|
p += delta;
|
||||||
|
return polygon;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_polygon min(size_polygon const & polygon, int dimension)
|
||||||
|
{
|
||||||
|
if (polygon.empty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
size_polygon result;
|
||||||
|
|
||||||
|
auto cit = util::make_cyclic_iterator(polygon);
|
||||||
|
std::size_t iterations = 0;
|
||||||
|
|
||||||
|
for (auto next = std::next(cit); (*next)[dimension] <= (*cit)[dimension] && iterations < polygon.size(); cit = next++, ++iterations);
|
||||||
|
|
||||||
|
if (iterations == polygon.size())
|
||||||
|
return polygon;
|
||||||
|
|
||||||
|
for (auto prev = std::prev(cit); (*prev)[dimension] <= (*cit)[dimension]; cit = prev--);
|
||||||
|
|
||||||
|
for (auto cjt = cit; (*cjt)[dimension] == (*cit)[dimension]; ++cjt)
|
||||||
|
result.push_back(*cjt);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_polygon intersect(size_polygon const & polygon1, size_polygon const & polygon2)
|
||||||
|
{
|
||||||
|
// TODO: https://www.cs.jhu.edu/~misha/Spring16/ORourke82.pdf
|
||||||
|
(void)polygon1;
|
||||||
|
(void)polygon2;
|
||||||
|
util::not_implemented();
|
||||||
|
}
|
||||||
|
|
||||||
|
geom::interval<float> range(size_polygon const & polygon, int dimension)
|
||||||
|
{
|
||||||
|
geom::interval<float> result;
|
||||||
|
for (auto const & p : polygon)
|
||||||
|
result |= p[dimension];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_polygon cut(size_polygon const & polygon, int dimension, geom::interval<float> const & range)
|
||||||
|
{
|
||||||
|
size_polygon result;
|
||||||
|
std::size_t iterations = 0;
|
||||||
|
for (auto it = util::make_cyclic_iterator(polygon), next_it = std::next(it); iterations != polygon.size(); (it = next_it++), ++iterations)
|
||||||
|
{
|
||||||
|
auto const & curr = *it;
|
||||||
|
auto const & next = *next_it;
|
||||||
|
|
||||||
|
auto intersection = [&](float value)
|
||||||
|
{
|
||||||
|
// curr + (next - curr) * t = value
|
||||||
|
float t = (value - curr[dimension]) / (next[dimension] - curr[dimension]);
|
||||||
|
geom::point<float, 2> p;
|
||||||
|
p[dimension] = value;
|
||||||
|
p[dimension ^ 1] = geom::lerp(curr[dimension ^ 1], next[dimension ^ 1], t);
|
||||||
|
return p;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto classify = [&](float value)
|
||||||
|
{
|
||||||
|
if (value < range.min)
|
||||||
|
return -2;
|
||||||
|
else if (value == range.min)
|
||||||
|
return -1;
|
||||||
|
else if (value < range.max)
|
||||||
|
return 0;
|
||||||
|
else if (value == range.max)
|
||||||
|
return 1;
|
||||||
|
else // (value > range.max)
|
||||||
|
return 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
int const curr_class = classify(curr[dimension]);
|
||||||
|
int const next_class = classify(next[dimension]);
|
||||||
|
|
||||||
|
if (curr_class >= -1 && curr_class <= 1)
|
||||||
|
result.push_back(curr);
|
||||||
|
|
||||||
|
bool const min = (curr_class == -2 && next_class >= 0) || (curr_class >= 0 && next_class == -2);
|
||||||
|
bool const max = (curr_class == 2 && next_class <= 0) || (curr_class <= 0 && next_class == 2);
|
||||||
|
|
||||||
|
|
||||||
|
if (range.min == range.max)
|
||||||
|
{
|
||||||
|
if (min) result.push_back(intersection(range.min));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (min) result.push_back(intersection(range.min));
|
||||||
|
if (max) result.push_back(intersection(range.max));
|
||||||
|
|
||||||
|
if (min && max && curr_class == 2)
|
||||||
|
std::swap(result.back(), result[result.size() - 2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_polygon sum_along(size_polygon const & polygon1, size_polygon const & polygon2, int dimension)
|
||||||
|
{
|
||||||
|
if (polygon1.empty() || polygon2.empty())
|
||||||
|
return size_polygon{};
|
||||||
|
|
||||||
|
int const other_dimension = dimension ^ 1;
|
||||||
|
|
||||||
|
auto range1 = range(polygon1, other_dimension);
|
||||||
|
auto range2 = range(polygon2, other_dimension);
|
||||||
|
|
||||||
|
auto common_range = range1 & range2;
|
||||||
|
if (common_range.empty())
|
||||||
|
return size_polygon{};
|
||||||
|
|
||||||
|
auto cut1 = cut(polygon1, other_dimension, common_range);
|
||||||
|
auto cut2 = cut(polygon2, other_dimension, common_range);
|
||||||
|
|
||||||
|
util::not_implemented();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,33 @@
|
||||||
#include <psemek/ui/impl/stack_layout_base.hpp>
|
#include <psemek/ui/impl/stack_layout_base.hpp>
|
||||||
|
#include <psemek/ui/impl/size_polygon_utils.hpp>
|
||||||
|
#include <psemek/react/join.hpp>
|
||||||
|
#include <psemek/react/map.hpp>
|
||||||
|
|
||||||
namespace psemek::ui::impl
|
namespace psemek::ui::impl
|
||||||
{
|
{
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
size_polygon compute_size_constraints(std::vector<size_polygon const *> const & children_size_constraints)
|
||||||
|
{
|
||||||
|
if (children_size_constraints.empty())
|
||||||
|
return component::default_size_polygon();
|
||||||
|
|
||||||
|
auto result = *children_size_constraints.front();
|
||||||
|
for (std::size_t i = 1; i < children_size_constraints.size(); ++i)
|
||||||
|
result = intersect(result, *children_size_constraints[i]);
|
||||||
|
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_with_transform([](react::value<size_polygon> const & arg){ return &(*arg); }), children_size_constraints_))))
|
||||||
|
{}
|
||||||
|
|
||||||
void stack_layout_base::reshape(geom::box<float, 2> const & new_shape)
|
void stack_layout_base::reshape(geom::box<float, 2> const & new_shape)
|
||||||
{
|
{
|
||||||
for (auto const & child : children())
|
for (auto const & child : children())
|
||||||
|
|
@ -10,15 +35,31 @@ namespace psemek::ui::impl
|
||||||
child->reshape(new_shape);
|
child->reshape(new_shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
geom::interval<float> stack_layout_base::size_constraints(int dimension, float other_dimension_size) const
|
react::value<size_polygon> stack_layout_base::size_constraints() const
|
||||||
{
|
{
|
||||||
auto result = container::size_constraints(dimension, other_dimension_size);
|
return size_constraints_;
|
||||||
|
}
|
||||||
|
|
||||||
for (auto const & child : children())
|
void stack_layout_base::set_children(std::vector<std::unique_ptr<component>> children)
|
||||||
|
{
|
||||||
|
container::set_children(std::move(children));
|
||||||
|
std::vector<react::value<size_polygon>> children_size_constraints;
|
||||||
|
for (auto const & child : this->children())
|
||||||
|
{
|
||||||
if (child)
|
if (child)
|
||||||
result &= child->size_constraints(dimension, other_dimension_size);
|
children_size_constraints.push_back(child->size_constraints());
|
||||||
|
else
|
||||||
|
children_size_constraints.push_back(default_size_constraints());
|
||||||
|
}
|
||||||
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
#include <psemek/test/test.hpp>
|
#include <psemek/test/test.hpp>
|
||||||
|
|
||||||
#include <psemek/ui/impl/component_factory_base.hpp>
|
#include <psemek/ui/impl/component_factory_base.hpp>
|
||||||
#include <psemek/ui/impl/rectangle_base.hpp>
|
|
||||||
#include <psemek/ui/impl/single_container_base.hpp>
|
#include <psemek/ui/impl/single_container_base.hpp>
|
||||||
#include <psemek/ui/impl/stack_layout_base.hpp>
|
#include <psemek/ui/impl/stack_layout_base.hpp>
|
||||||
#include <psemek/ui/impl/box_layout_base.hpp>
|
#include <psemek/ui/impl/box_layout_base.hpp>
|
||||||
|
#include <psemek/ui/rectangle.hpp>
|
||||||
|
|
||||||
#include <psemek/react/source.hpp>
|
#include <psemek/react/source.hpp>
|
||||||
|
|
||||||
|
|
@ -15,14 +15,20 @@ namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
constexpr float layout_margin = 5.f;
|
constexpr float layout_margin = 5.f;
|
||||||
constexpr float infinity = impl::component::infinity;
|
|
||||||
|
struct rectangle_impl
|
||||||
|
: impl::component
|
||||||
|
{
|
||||||
|
void update(rectangle const &)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
struct test_component_factory
|
struct test_component_factory
|
||||||
: impl::component_factory_base
|
: impl::component_factory_base
|
||||||
{
|
{
|
||||||
test_component_factory()
|
test_component_factory()
|
||||||
{
|
{
|
||||||
register_type<rectangle, impl::rectangle_base>();
|
register_type<rectangle, rectangle_impl>();
|
||||||
register_type<stack_layout, impl::stack_layout_base>();
|
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::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); });
|
register_type<box_layout::vertical, impl::box_layout_base<1>>([]{ return std::make_unique<impl::box_layout_base<1>>(layout_margin); });
|
||||||
|
|
@ -30,63 +36,3 @@ namespace
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test_case(ui_layout_rectangle)
|
|
||||||
{
|
|
||||||
test_component_factory factory;
|
|
||||||
|
|
||||||
auto square = react::source(false);
|
|
||||||
|
|
||||||
auto test_ui = rectangle{.square = square};
|
|
||||||
|
|
||||||
auto ui_root = factory.reconciliate(nullptr, test_ui);
|
|
||||||
|
|
||||||
expect_equal(ui_root->size_constraints(0, 100.f), geom::interval<float>(0.f, infinity));
|
|
||||||
expect_equal(ui_root->size_constraints(1, 100.f), geom::interval<float>(0.f, infinity));
|
|
||||||
|
|
||||||
square.set(true);
|
|
||||||
|
|
||||||
expect_equal(ui_root->size_constraints(0, 100.f), geom::interval<float>(100.f, 100.f));
|
|
||||||
expect_equal(ui_root->size_constraints(1, 100.f), geom::interval<float>(100.f, 100.f));
|
|
||||||
|
|
||||||
ui_root->reshape({{{0.f, 10.f}, {0.f, 10.f}}});
|
|
||||||
|
|
||||||
expect_equal(ui_root->shape(), (geom::box<float, 2>{{{0.f, 10.f}, {0.f, 10.f}}}));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
test_case(ui_layout_hbox__2__rectangles)
|
|
||||||
{
|
|
||||||
test_component_factory factory;
|
|
||||||
|
|
||||||
auto square0 = react::source(false);
|
|
||||||
auto square1 = react::source(false);
|
|
||||||
|
|
||||||
auto policy0 = react::source<box_layout::size_policy>(box_layout::minimized{});
|
|
||||||
auto policy1 = react::source<box_layout::size_policy>(box_layout::minimized{});
|
|
||||||
|
|
||||||
auto test_ui = box_layout::horizontal{{
|
|
||||||
{
|
|
||||||
.element = rectangle{.square = square0},
|
|
||||||
.policy = policy0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.element = rectangle{.square = square1},
|
|
||||||
.policy = policy1,
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
|
|
||||||
auto ui_root = factory.reconciliate(nullptr, test_ui);
|
|
||||||
|
|
||||||
expect_equal(ui_root->size_constraints(0, 100.f), geom::interval<float>(layout_margin, infinity));
|
|
||||||
expect_equal(ui_root->size_constraints(1, 100.f), geom::interval<float>(0.f, infinity));
|
|
||||||
|
|
||||||
square0.set(true);
|
|
||||||
|
|
||||||
expect_equal(ui_root->size_constraints(0, 100.f), geom::interval<float>(100.f + layout_margin, infinity));
|
|
||||||
expect_equal(ui_root->size_constraints(1, 100.f), geom::interval<float>(100.f, 100.f));
|
|
||||||
|
|
||||||
ui_root->reshape({{{0.f, 10.f}, {0.f, 10.f}}});
|
|
||||||
|
|
||||||
expect_equal(ui_root->shape(), (geom::box<float, 2>{{{0.f, 10.f}, {0.f, 10.f}}}));
|
|
||||||
}
|
|
||||||
|
|
|
||||||
233
libs/ui/tests/size_polygon_cut.cpp
Normal file
233
libs/ui/tests/size_polygon_cut.cpp
Normal file
|
|
@ -0,0 +1,233 @@
|
||||||
|
#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;
|
||||||
|
}
|
||||||
95
libs/ui/tests/size_polygon_min.cpp
Normal file
95
libs/ui/tests/size_polygon_min.cpp
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
#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