UI library wip: event handling, auto-layout, basic layouts & buttons
This commit is contained in:
parent
546c0f2a7b
commit
ae815ec538
25 changed files with 547 additions and 32 deletions
|
|
@ -3,6 +3,6 @@ file(GLOB_RECURSE PSEMEK_UI_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "sour
|
|||
|
||||
psemek_add_library(psemek-ui ${PSEMEK_UI_HEADERS} ${PSEMEK_UI_SOURCES})
|
||||
target_include_directories(psemek-ui PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_link_libraries(psemek-ui PUBLIC psemek-util psemek-react psemek-geom psemek-gfx)
|
||||
target_link_libraries(psemek-ui PUBLIC psemek-util psemek-react psemek-geom psemek-gfx psemek-app)
|
||||
|
||||
psemek_glob_tests(psemek-ui tests)
|
||||
|
|
|
|||
18
libs/ui/include/psemek/ui/aligned.hpp
Normal file
18
libs/ui/include/psemek/ui/aligned.hpp
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#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,5 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/geom/interval.hpp>
|
||||
#include <psemek/util/enum.hpp>
|
||||
|
||||
namespace psemek::ui
|
||||
{
|
||||
|
||||
|
|
@ -17,4 +20,43 @@ namespace psemek::ui
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,12 +33,14 @@ namespace psemek::ui
|
|||
{
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,15 +3,13 @@
|
|||
#include <psemek/react/value.hpp>
|
||||
#include <psemek/react/source.hpp>
|
||||
|
||||
#include <any>
|
||||
|
||||
namespace psemek::ui
|
||||
{
|
||||
|
||||
struct button
|
||||
{
|
||||
react::source<void> on_click = {};
|
||||
react::value<std::any> child = {};
|
||||
react::source<bool> mouseover = {};
|
||||
react::source<bool> mousedown = {};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
29
libs/ui/include/psemek/ui/fixed_size.hpp
Normal file
29
libs/ui/include/psemek/ui/fixed_size.hpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#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;
|
||||
};
|
||||
|
||||
}
|
||||
29
libs/ui/include/psemek/ui/impl/aligned_base.hpp
Normal file
29
libs/ui/include/psemek/ui/impl/aligned_base.hpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -34,7 +34,7 @@ namespace psemek::ui::impl
|
|||
struct box_layout_base
|
||||
: container
|
||||
{
|
||||
box_layout_base(react::value<float> margin);
|
||||
box_layout_base();
|
||||
|
||||
void reshape(geom::box<float, 2> const & new_shape) override;
|
||||
react::value<struct size_constraints> size_constraints() const override;
|
||||
|
|
@ -45,7 +45,7 @@ namespace psemek::ui::impl
|
|||
void update(typename detail::box_layout_type<Dimension>::type const & box_layout);
|
||||
|
||||
private:
|
||||
react::value<float> margin_;
|
||||
react::source<react::value<float>> padding_;
|
||||
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_;
|
||||
|
|
|
|||
27
libs/ui/include/psemek/ui/impl/button_base.hpp
Normal file
27
libs/ui/include/psemek/ui/impl/button_base.hpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#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:
|
||||
bool is_mouseover_ = false;
|
||||
bool is_mousedown_ = false;
|
||||
|
||||
react::source<bool> mouseover_;
|
||||
react::source<bool> mousedown_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ui/impl/size_constraints.hpp>
|
||||
#include <psemek/ui/impl/events.hpp>
|
||||
#include <psemek/react/value.hpp>
|
||||
#include <psemek/geom/box.hpp>
|
||||
#include <psemek/geom/interval.hpp>
|
||||
|
|
@ -13,8 +14,6 @@ namespace psemek::ui::impl
|
|||
|
||||
struct component
|
||||
{
|
||||
static constexpr float infinity = std::numeric_limits<float>::infinity();
|
||||
|
||||
virtual util::span<std::unique_ptr<component> const> children() const;
|
||||
|
||||
virtual geom::box<float, 2> const & shape() const;
|
||||
|
|
@ -22,6 +21,12 @@ namespace psemek::ui::impl
|
|||
|
||||
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 ~component() {}
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -16,6 +16,12 @@ namespace psemek::ui::impl
|
|||
|
||||
void set_ui(react::value<std::any> ui);
|
||||
|
||||
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);
|
||||
|
||||
private:
|
||||
psemek_declare_pimpl
|
||||
};
|
||||
|
|
|
|||
14
libs/ui/include/psemek/ui/impl/default_component_factory.hpp
Normal file
14
libs/ui/include/psemek/ui/impl/default_component_factory.hpp
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ui/impl/component_factory_base.hpp>
|
||||
|
||||
namespace psemek::ui::impl
|
||||
{
|
||||
|
||||
struct default_component_factory
|
||||
: component_factory_base
|
||||
{
|
||||
default_component_factory();
|
||||
};
|
||||
|
||||
}
|
||||
14
libs/ui/include/psemek/ui/impl/events.hpp
Normal file
14
libs/ui/include/psemek/ui/impl/events.hpp
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#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;
|
||||
|
||||
}
|
||||
28
libs/ui/include/psemek/ui/impl/fixed_size_base.hpp
Normal file
28
libs/ui/include/psemek/ui/impl/fixed_size_base.hpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include <psemek/geom/box.hpp>
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace psemek::ui::impl
|
||||
{
|
||||
|
||||
|
|
@ -9,6 +11,8 @@ namespace psemek::ui::impl
|
|||
{
|
||||
geom::box<float, 2> box;
|
||||
|
||||
static constexpr float infinity = std::numeric_limits<float>::infinity();
|
||||
|
||||
static size_constraints max();
|
||||
};
|
||||
|
||||
|
|
|
|||
71
libs/ui/source/impl/aligned_base.cpp
Normal file
71
libs/ui/source/impl/aligned_base.cpp
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
#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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ namespace psemek::ui::impl
|
|||
|
||||
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 margin)
|
||||
std::vector<size_constraints> const & children_size_constraints, float padding)
|
||||
{
|
||||
size_constraints minimized = size_constraints::max();
|
||||
size_constraints weight_unit = minimized;
|
||||
|
|
@ -39,7 +39,7 @@ namespace psemek::ui::impl
|
|||
if (size_policies.size() > 0)
|
||||
{
|
||||
geom::vector shift_delta{0.f, 0.f};
|
||||
shift_delta[Dimension] = margin * (size_policies.size() - 1);
|
||||
shift_delta[Dimension] = padding * (size_policies.size() - 1);
|
||||
result = shift(std::move(result), shift_delta);
|
||||
}
|
||||
|
||||
|
|
@ -47,18 +47,21 @@ namespace psemek::ui::impl
|
|||
}
|
||||
|
||||
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> 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(policies.size(), 0.f);
|
||||
std::vector<float> result(size_policies.size(), 0.f);
|
||||
|
||||
if (!size_policies.empty())
|
||||
total_size -= padding * (size_policies.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 < policies.size(); ++i)
|
||||
for (std::size_t i = 0; i < size_policies.size(); ++i)
|
||||
{
|
||||
auto const policy = policies[i] ? *policies[i] : default_policy;
|
||||
auto const policy = size_policies[i] ? *size_policies[i] : default_policy;
|
||||
|
||||
if (std::get_if<box_layout::minimized>(&policy))
|
||||
{
|
||||
|
|
@ -72,9 +75,9 @@ namespace psemek::ui::impl
|
|||
}
|
||||
|
||||
// Next, allocate weighted elements
|
||||
for (std::size_t i = 0; i < policies.size(); ++i)
|
||||
for (std::size_t i = 0; i < size_policies.size(); ++i)
|
||||
{
|
||||
auto const policy = policies[i] ? *policies[i] : default_policy;
|
||||
auto const policy = size_policies[i] ? *size_policies[i] : default_policy;
|
||||
|
||||
if (auto weight = std::get_if<box_layout::weight>(&policy))
|
||||
{
|
||||
|
|
@ -88,18 +91,18 @@ namespace psemek::ui::impl
|
|||
}
|
||||
|
||||
template <int Dimension>
|
||||
box_layout_base<Dimension>::box_layout_base(react::value<float> margin)
|
||||
: margin_(margin)
|
||||
box_layout_base<Dimension>::box_layout_base()
|
||||
: padding_(0.f)
|
||||
, 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);
|
||||
[](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), size_policies_)),
|
||||
react::join(react::map(react::unpack, children_size_constraints_)),
|
||||
margin_
|
||||
react::join(padding_)
|
||||
)
|
||||
)
|
||||
{}
|
||||
|
|
@ -112,9 +115,9 @@ namespace psemek::ui::impl
|
|||
if (children().empty())
|
||||
return;
|
||||
|
||||
float const margin = *margin_;
|
||||
auto padding = **padding_;
|
||||
|
||||
auto sizes = allocate<Dimension>(new_shape[Dimension].length() - (children().size() - 1) * margin, *size_policies_, children());
|
||||
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)
|
||||
|
|
@ -127,7 +130,7 @@ namespace psemek::ui::impl
|
|||
children()[i]->reshape(child_shape);
|
||||
|
||||
pen += sizes[i];
|
||||
pen += margin;
|
||||
pen += padding;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -171,6 +174,8 @@ namespace psemek::ui::impl
|
|||
size_policies.push_back(child.policy);
|
||||
return size_policies;
|
||||
}, box_layout.children);
|
||||
|
||||
padding_.set(box_layout.padding);
|
||||
}
|
||||
|
||||
template struct box_layout_base<0>;
|
||||
|
|
|
|||
47
libs/ui/source/impl/button_base.cpp
Normal file
47
libs/ui/source/impl/button_base.cpp
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
#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_);
|
||||
}
|
||||
|
||||
return is_mouseover_;
|
||||
}
|
||||
|
||||
bool button_base::on_event(mouse_button_event const & event)
|
||||
{
|
||||
if (is_mouseover_)
|
||||
{
|
||||
bool const mousedown = event.button == app::mouse_button::left && event.down;
|
||||
|
||||
if (is_mousedown_ != mousedown)
|
||||
{
|
||||
is_mousedown_ = mousedown;
|
||||
mousedown_.set(is_mouseover_);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void button_base::update(button const & value)
|
||||
{
|
||||
mouseover_ = value.mouseover;
|
||||
mousedown_ = value.mousedown;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
#include <psemek/ui/impl/container.hpp>
|
||||
#include <psemek/util/range.hpp>
|
||||
|
||||
namespace psemek::ui::impl
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,9 +7,69 @@ namespace psemek::ui::impl
|
|||
{
|
||||
component_factory & factory;
|
||||
|
||||
geom::vector<int, 2> screen_size = {0, 0};
|
||||
|
||||
util::signal<>::subscription_token root_token;
|
||||
util::signal<>::subscription_token root_reshape_token;
|
||||
std::unique_ptr<ui::impl::component> root;
|
||||
|
||||
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);
|
||||
subscribe_reshape();
|
||||
}, true);
|
||||
subscribe_reshape();
|
||||
}
|
||||
|
||||
void subscribe_reshape()
|
||||
{
|
||||
root_reshape_token = nullptr;
|
||||
|
||||
if (root)
|
||||
root_reshape_token = root->size_constraints().subscribe([this](psemek::ui::impl::size_constraints const &)
|
||||
{
|
||||
do_reshape();
|
||||
});
|
||||
}
|
||||
|
||||
void do_reshape()
|
||||
{
|
||||
if (root)
|
||||
root->reshape({{{0.f, screen_size[0]}, {0.f, screen_size[1]}}});
|
||||
}
|
||||
|
||||
template <typename Event>
|
||||
bool on_event(Event const & event)
|
||||
{
|
||||
return on_event_impl(event, root.get());
|
||||
}
|
||||
|
||||
bool on_event(resize_event const & event)
|
||||
{
|
||||
screen_size = event.size;
|
||||
bool result = on_event_impl(event, root.get());
|
||||
do_reshape();
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Event>
|
||||
bool on_event_impl(Event const & event, component * element)
|
||||
{
|
||||
if (!element)
|
||||
return false;
|
||||
|
||||
for (auto const & child : element->children())
|
||||
if (on_event_impl(event, child.get()))
|
||||
return true;
|
||||
|
||||
return element->on_event(event);
|
||||
}
|
||||
};
|
||||
|
||||
controller::controller(component_factory & factory)
|
||||
|
|
@ -20,7 +80,32 @@ namespace psemek::ui::impl
|
|||
|
||||
void controller::set_ui(react::value<std::any> ui)
|
||||
{
|
||||
(void)ui;
|
||||
impl().set_ui(std::move(ui));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
28
libs/ui/source/impl/default_component_factory.cpp
Normal file
28
libs/ui/source/impl/default_component_factory.cpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#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/button_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<button, impl::button_base>();
|
||||
}
|
||||
|
||||
}
|
||||
65
libs/ui/source/impl/fixed_size_base.cpp
Normal file
65
libs/ui/source/impl/fixed_size_base.cpp
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -39,5 +39,4 @@ namespace psemek::ui::impl
|
|||
return child;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,10 @@
|
|||
#include <psemek/ui/impl/size_constraints.hpp>
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace psemek::ui::impl
|
||||
{
|
||||
|
||||
size_constraints size_constraints::max()
|
||||
{
|
||||
static constexpr float infinity = std::numeric_limits<float>::infinity();
|
||||
|
||||
return
|
||||
{{{
|
||||
{0.f, infinity},
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ namespace psemek::ui::impl
|
|||
|
||||
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);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue